diff --git a/FCL/src/main/java/com/tungsten/fcl/game/FCLGameRepository.java b/FCL/src/main/java/com/tungsten/fcl/game/FCLGameRepository.java index 1ecadd56..8f11685e 100644 --- a/FCL/src/main/java/com/tungsten/fcl/game/FCLGameRepository.java +++ b/FCL/src/main/java/com/tungsten/fcl/game/FCLGameRepository.java @@ -65,7 +65,7 @@ public class FCLGameRepository extends DefaultGameRepository { if (beingModpackVersions.contains(id) || isModpack(id)) { return GameDirectoryType.VERSION_FOLDER; } else { - return getVersionSetting(id).getGameDirType(); + return getVersionSetting(id).isIsolateGameDir() ? GameDirectoryType.VERSION_FOLDER : GameDirectoryType.ROOT_FOLDER; } } @@ -147,9 +147,9 @@ public class FCLGameRepository extends DefaultGameRepository { FileUtils.writeText(toJson.toFile(), JsonUtils.GSON.toJson(fromVersion.setId(dstId))); VersionSetting oldVersionSetting = getVersionSetting(srcId).clone(); - GameDirectoryType originalGameDirType = oldVersionSetting.getGameDirType(); + GameDirectoryType originalGameDirType = (oldVersionSetting.isIsolateGameDir() ? GameDirectoryType.VERSION_FOLDER : GameDirectoryType.ROOT_FOLDER); oldVersionSetting.setUsesGlobal(false); - oldVersionSetting.setGameDirType(GameDirectoryType.VERSION_FOLDER); + oldVersionSetting.setIsolateGameDir(true); VersionSetting newVersionSetting = initLocalVersionSetting(dstId, oldVersionSetting); saveVersionSetting(dstId); @@ -225,7 +225,7 @@ public class FCLGameRepository extends DefaultGameRepository { loadLocalVersionSetting(id); VersionSetting setting = localVersionSettings.get(id); if (setting != null && isModpack(id)) - setting.setGameDirType(GameDirectoryType.VERSION_FOLDER); + setting.setIsolateGameDir(true); return setting; } @@ -336,7 +336,7 @@ public class FCLGameRepository extends DefaultGameRepository { .setServerIp(vs.getServerIp()) .setProcessPriority(vs.getProcessPriority()) .setJavaAgents(javaAgents) - .setRenderer(vs.getRender()); + .setRenderer(vs.getRenderer()); File json = getModpackConfiguration(version); if (json.exists()) { diff --git a/FCL/src/main/java/com/tungsten/fcl/game/ModpackHelper.java b/FCL/src/main/java/com/tungsten/fcl/game/ModpackHelper.java index c499a0a3..d6f29a03 100644 --- a/FCL/src/main/java/com/tungsten/fcl/game/ModpackHelper.java +++ b/FCL/src/main/java/com/tungsten/fcl/game/ModpackHelper.java @@ -148,7 +148,7 @@ public final class ModpackHelper { VersionSetting vs = repository.specializeVersionSetting(name); repository.undoMark(name); if (vs != null) - vs.setGameDirType(GameDirectoryType.VERSION_FOLDER); + vs.setIsolateGameDir(true); }; ExceptionalConsumer failure = ex -> { @@ -189,7 +189,7 @@ public final class ModpackHelper { VersionSetting vs = repository.specializeVersionSetting(name); repository.undoMark(name); if (vs != null) - vs.setGameDirType(GameDirectoryType.VERSION_FOLDER); + vs.setIsolateGameDir(true); }; ExceptionalConsumer failure = ex -> { @@ -233,7 +233,7 @@ public final class ModpackHelper { public static void toVersionSetting(MultiMCInstanceConfiguration c, VersionSetting vs) { vs.setUsesGlobal(false); - vs.setGameDirType(GameDirectoryType.VERSION_FOLDER); + vs.setIsolateGameDir(true); if (c.isOverrideMemory()) { vs.setPermSize(Optional.ofNullable(c.getPermGen()).map(Object::toString).orElse("")); diff --git a/FCL/src/main/java/com/tungsten/fcl/setting/VersionSetting.java b/FCL/src/main/java/com/tungsten/fcl/setting/VersionSetting.java index 5d6cd91f..a95e53eb 100644 --- a/FCL/src/main/java/com/tungsten/fcl/setting/VersionSetting.java +++ b/FCL/src/main/java/com/tungsten/fcl/setting/VersionSetting.java @@ -301,18 +301,35 @@ public final class VersionSetting implements Cloneable { * 0 - .minecraft
* 1 - .minecraft/versions/<version>/
*/ - private final ObjectProperty gameDirTypeProperty = new SimpleObjectProperty<>(this, "gameDirType", GameDirectoryType.ROOT_FOLDER); + private final BooleanProperty isolateGameDirProperty = new SimpleBooleanProperty(this, "isolateGameDir", false); - public ObjectProperty gameDirTypeProperty() { - return gameDirTypeProperty; + public BooleanProperty isolateGameDirProperty() { + return isolateGameDirProperty; } - public GameDirectoryType getGameDirType() { - return gameDirTypeProperty.get(); + public boolean isIsolateGameDir() { + return isolateGameDirProperty.get(); } - public void setGameDirType(GameDirectoryType gameDirType) { - gameDirTypeProperty.set(gameDirType); + public void setIsolateGameDir(boolean isolateGameDir) { + isolateGameDirProperty.set(isolateGameDir); + } + + private final BooleanProperty beGestureProperty = new SimpleBooleanProperty(this, "beGesture", false); + + public BooleanProperty beGestureProperty() { + return beGestureProperty; + } + + /** + * True if FCL does not check game's completeness. + */ + public boolean isBeGesture() { + return beGestureProperty.get(); + } + + public void setBeGesture(boolean beGesture) { + beGestureProperty.set(beGesture); } private final ObjectProperty processPriorityProperty = new SimpleObjectProperty<>(this, "processPriority", ProcessPriority.NORMAL); @@ -331,11 +348,11 @@ public final class VersionSetting implements Cloneable { private final ObjectProperty rendererProperty = new SimpleObjectProperty<>(this, "render", FCLConfig.Renderer.RENDERER_GL4ES); - public ObjectProperty renderProperty() { + public ObjectProperty rendererProperty() { return rendererProperty; } - public FCLConfig.Renderer getRender() { + public FCLConfig.Renderer getRenderer() { return rendererProperty.get(); } @@ -374,7 +391,8 @@ public final class VersionSetting implements Cloneable { fullscreenProperty.addListener(listener); widthProperty.addListener(listener); heightProperty.addListener(listener); - gameDirTypeProperty.addListener(listener); + isolateGameDirProperty.addListener(listener); + beGestureProperty.addListener(listener); processPriorityProperty.addListener(listener); rendererProperty.addListener(listener); } @@ -396,9 +414,10 @@ public final class VersionSetting implements Cloneable { versionSetting.setFullscreen(isFullscreen()); versionSetting.setWidth(getWidth()); versionSetting.setHeight(getHeight()); - versionSetting.setGameDirType(getGameDirType()); + versionSetting.setIsolateGameDir(isIsolateGameDir()); + versionSetting.setBeGesture(isBeGesture()); versionSetting.setProcessPriority(getProcessPriority()); - versionSetting.setRenderer(getRender()); + versionSetting.setRenderer(getRenderer()); return versionSetting; } @@ -422,9 +441,10 @@ public final class VersionSetting implements Cloneable { obj.addProperty("fullscreen", src.isFullscreen()); obj.addProperty("notCheckGame", src.isNotCheckGame()); obj.addProperty("notCheckJVM", src.isNotCheckJVM()); + obj.addProperty("beGesture", src.isBeGesture()); obj.addProperty("processPriority", src.getProcessPriority().ordinal()); - obj.addProperty("renderer", src.getRender().ordinal()); - obj.addProperty("gameDirType", src.getGameDirType().ordinal()); + obj.addProperty("renderer", src.getRenderer().ordinal()); + obj.addProperty("isolateGameDir", src.isIsolateGameDir()); return obj; } @@ -454,9 +474,10 @@ public final class VersionSetting implements Cloneable { vs.setFullscreen(Optional.ofNullable(obj.get("fullscreen")).map(JsonElement::getAsBoolean).orElse(false)); vs.setNotCheckGame(Optional.ofNullable(obj.get("notCheckGame")).map(JsonElement::getAsBoolean).orElse(false)); vs.setNotCheckJVM(Optional.ofNullable(obj.get("notCheckJVM")).map(JsonElement::getAsBoolean).orElse(false)); + vs.setNotCheckJVM(Optional.ofNullable(obj.get("beGesture")).map(JsonElement::getAsBoolean).orElse(false)); vs.setProcessPriority(ProcessPriority.values()[Optional.ofNullable(obj.get("processPriority")).map(JsonElement::getAsInt).orElse(ProcessPriority.NORMAL.ordinal())]); vs.setRenderer(FCLConfig.Renderer.values()[Optional.ofNullable(obj.get("renderer")).map(JsonElement::getAsInt).orElse(FCLConfig.Renderer.RENDERER_GL4ES.ordinal())]); - vs.setGameDirType(GameDirectoryType.values()[Optional.ofNullable(obj.get("gameDirType")).map(JsonElement::getAsInt).orElse(GameDirectoryType.ROOT_FOLDER.ordinal())]); + vs.setIsolateGameDir(Optional.ofNullable(obj.get("isolateGameDir")).map(JsonElement::getAsBoolean).orElse(false)); return vs; } diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/download/RemoteVersionListAdapter.java b/FCL/src/main/java/com/tungsten/fcl/ui/download/RemoteVersionListAdapter.java index b85c86b8..2245b5c3 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/download/RemoteVersionListAdapter.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/download/RemoteVersionListAdapter.java @@ -2,7 +2,6 @@ package com.tungsten.fcl.ui.download; import android.annotation.SuppressLint; import android.content.Context; -import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; @@ -19,7 +18,6 @@ import com.tungsten.fclcore.download.optifine.OptiFineRemoteVersion; import com.tungsten.fclcore.download.quilt.QuiltAPIRemoteVersion; import com.tungsten.fclcore.download.quilt.QuiltRemoteVersion; import com.tungsten.fcllibrary.component.FCLAdapter; -import com.tungsten.fcllibrary.component.theme.ThemeEngine; import com.tungsten.fcllibrary.component.view.FCLImageView; import com.tungsten.fcllibrary.component.view.FCLLinearLayout; import com.tungsten.fcllibrary.component.view.FCLTextView; @@ -74,7 +72,6 @@ public class RemoteVersionListAdapter extends FCLAdapter { viewHolder = (ViewHolder) view.getTag(); } RemoteVersion remoteVersion = list.get(i); - ThemeEngine.getInstance().registerEvent(viewHolder.tag, () -> viewHolder.tag.setBackgroundTintList(new ColorStateList(new int[][]{ { } }, new int[]{ ThemeEngine.getInstance().getTheme().getColor() }))); viewHolder.parent.setOnClickListener(view1 -> listener.onSelect(remoteVersion)); viewHolder.icon.setBackground(getIcon(remoteVersion)); viewHolder.version.setText(remoteVersion.getSelfVersion()); diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/manage/SelectIconDialog.java b/FCL/src/main/java/com/tungsten/fcl/ui/manage/SelectIconDialog.java new file mode 100644 index 00000000..332b3d5a --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/ui/manage/SelectIconDialog.java @@ -0,0 +1,14 @@ +package com.tungsten.fcl.ui.manage; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.tungsten.fcllibrary.component.dialog.FCLDialog; + +public class SelectIconDialog extends FCLDialog { + + public SelectIconDialog(@NonNull Context context) { + super(context); + } +} diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/manage/VersionSettingPage.java b/FCL/src/main/java/com/tungsten/fcl/ui/manage/VersionSettingPage.java index c28b43b2..6bf1e9eb 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/manage/VersionSettingPage.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/manage/VersionSettingPage.java @@ -2,31 +2,41 @@ package com.tungsten.fcl.ui.manage; import android.content.Context; import android.view.View; - -import androidx.appcompat.widget.AppCompatSpinner; +import android.widget.ArrayAdapter; import com.tungsten.fcl.R; import com.tungsten.fcl.setting.Profile; import com.tungsten.fcl.setting.VersionSetting; +import com.tungsten.fcl.util.FXUtils; import com.tungsten.fcl.util.WeakListenerHolder; +import com.tungsten.fclauncher.FCLConfig; +import com.tungsten.fclcore.fakefx.beans.InvalidationListener; import com.tungsten.fclcore.fakefx.beans.property.BooleanProperty; import com.tungsten.fclcore.fakefx.beans.property.IntegerProperty; import com.tungsten.fclcore.fakefx.beans.property.SimpleBooleanProperty; import com.tungsten.fclcore.fakefx.beans.property.SimpleIntegerProperty; import com.tungsten.fclcore.fakefx.beans.property.SimpleStringProperty; import com.tungsten.fclcore.fakefx.beans.property.StringProperty; +import com.tungsten.fclcore.game.JavaVersion; +import com.tungsten.fclcore.game.ProcessPriority; +import com.tungsten.fclcore.task.Schedulers; import com.tungsten.fclcore.task.Task; import com.tungsten.fcllibrary.component.ui.FCLCommonPage; import com.tungsten.fcllibrary.component.view.FCLCheckBox; import com.tungsten.fcllibrary.component.view.FCLEditText; import com.tungsten.fcllibrary.component.view.FCLImageButton; +import com.tungsten.fcllibrary.component.view.FCLImageView; import com.tungsten.fcllibrary.component.view.FCLLinearLayout; import com.tungsten.fcllibrary.component.view.FCLProgressBar; import com.tungsten.fcllibrary.component.view.FCLSeekBar; +import com.tungsten.fcllibrary.component.view.FCLSpinner; import com.tungsten.fcllibrary.component.view.FCLSwitch; import com.tungsten.fcllibrary.component.view.FCLTextView; import com.tungsten.fcllibrary.component.view.FCLUILayout; +import java.io.File; +import java.util.ArrayList; + public class VersionSettingPage extends FCLCommonPage implements ManageUI.VersionLoadable { private final boolean globalSetting; @@ -49,6 +59,8 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio private FCLCheckBox chkAutoAllocate; private FCLCheckBox chkFullscreen; + private FCLImageView iconView; + private FCLSeekBar allocateSeekbar; private FCLProgressBar memoryBar; @@ -59,17 +71,19 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio private FCLSwitch noGameCheckSwitch; private FCLSwitch noJVMCheckSwitch; - private AppCompatSpinner javaSpinner; - private AppCompatSpinner processPrioritySpinner; - private AppCompatSpinner rendererSpinner; + private FCLSpinner javaSpinner; + private FCLSpinner processPrioritySpinner; + private FCLSpinner rendererSpinner; private FCLImageButton editIconButton; + private FCLImageButton deleteIconButton; private FCLImageButton controllerButton; private FCLTextView memoryText; private FCLTextView memoryInfoText; private FCLTextView memoryAllocateText; + private final InvalidationListener specificSettingsListener; private final StringProperty selectedVersion = new SimpleStringProperty(); private final BooleanProperty enableSpecificSettings = new SimpleBooleanProperty(false); private final IntegerProperty maxMemory = new SimpleIntegerProperty(); @@ -80,6 +94,7 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio super(context, id, parent, resId); this.globalSetting = globalSetting; create(); + specificSettingsListener = any -> enableSpecificSettings.set(!lastVersionSetting.isUsesGlobal()); } private void create() { @@ -96,11 +111,14 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio chkAutoAllocate = findViewById(R.id.edit_auto_allocate); chkFullscreen = findViewById(R.id.edit_fullscreen); + iconView = findViewById(R.id.icon); + allocateSeekbar = findViewById(R.id.edit_memory); memoryBar = findViewById(R.id.memory_bar); specialSettingSwitch = findViewById(R.id.enable_per_instance_setting); + specialSettingSwitch.addCheckedChangeListener(); isolateWorkingDirSwitch = findViewById(R.id.edit_game_dir); beGestureSwitch = findViewById(R.id.edit_controller_injector); noGameCheckSwitch = findViewById(R.id.edit_not_check_game); @@ -110,7 +128,52 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio processPrioritySpinner = findViewById(R.id.edit_process_priority); rendererSpinner = findViewById(R.id.edit_renderer); + // add spinner data + ArrayList javaVersionDataList = new ArrayList<>(); + javaVersionDataList.add(JavaVersion.JAVA_AUTO.getId()); + javaVersionDataList.add(JavaVersion.JAVA_8.getId()); + javaVersionDataList.add(JavaVersion.JAVA_17.getId()); + javaSpinner.setDataList(javaVersionDataList); + + ArrayList processPriorityDataList = new ArrayList<>(); + processPriorityDataList.add(ProcessPriority.LOW); + processPriorityDataList.add(ProcessPriority.NORMAL); + processPriorityDataList.add(ProcessPriority.HIGH); + processPrioritySpinner.setDataList(processPriorityDataList); + + ArrayList rendererDataList = new ArrayList<>(); + rendererDataList.add(FCLConfig.Renderer.RENDERER_GL4ES); + rendererDataList.add(FCLConfig.Renderer.RENDERER_ZINK); + rendererDataList.add(FCLConfig.Renderer.RENDERER_ANGLE); + rendererSpinner.setDataList(rendererDataList); + + // add spinner text + ArrayList javaVersionList = new ArrayList<>(); + javaVersionList.add(getContext().getString(R.string.settings_game_java_version_auto)); + javaVersionList.add("JRE 8"); + javaVersionList.add("JRE 17"); + ArrayAdapter javaAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner, javaVersionList); + javaAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown); + javaSpinner.setAdapter(javaAdapter); + + ArrayList processPriorityList = new ArrayList<>(); + processPriorityList.add(getContext().getString(R.string.settings_advanced_process_priority_low)); + processPriorityList.add(getContext().getString(R.string.settings_advanced_process_priority_normal)); + processPriorityList.add(getContext().getString(R.string.settings_advanced_process_priority_high)); + ArrayAdapter processPriorityAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner, processPriorityList); + processPriorityAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown); + processPrioritySpinner.setAdapter(processPriorityAdapter); + + ArrayList rendererList = new ArrayList<>(); + rendererList.add(getContext().getString(R.string.settings_fcl_renderer_gl4es)); + rendererList.add(getContext().getString(R.string.settings_fcl_renderer_virgl)); + rendererList.add(getContext().getString(R.string.settings_fcl_renderer_angle)); + ArrayAdapter rendererAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner, rendererList); + rendererAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown); + rendererSpinner.setAdapter(rendererAdapter); + editIconButton = findViewById(R.id.edit_icon); + deleteIconButton = findViewById(R.id.delete_icon); controllerButton = findViewById(R.id.edit_controller); memoryText = findViewById(R.id.memory_text); @@ -120,8 +183,23 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio settingTypeLayout.setVisibility(globalSetting ? View.GONE : View.VISIBLE); if (!globalSetting) { - + specialSettingSwitch.disableProperty().bind(modpack); + specialSettingSwitch.checkProperty().bindBidirectional(enableSpecificSettings); } + + enableSpecificSettings.addListener((a, b, newValue) -> { + if (versionId == null) return; + + // do not call versionSettings.setUsesGlobal(true/false) + // because versionSettings can be the global one. + // global versionSettings.usesGlobal is always true. + if (newValue) + profile.getRepository().specializeVersionSetting(versionId); + else + profile.getRepository().globalizeVersionSetting(versionId); + + Schedulers.androidUIThread().execute(() -> loadVersion(profile, versionId)); + }); } @Override @@ -130,7 +208,93 @@ public class VersionSettingPage extends FCLCommonPage implements ManageUI.Versio } @Override - public void loadVersion(Profile profile, String version) { + public void loadVersion(Profile profile, String versionId) { + this.profile = profile; + this.versionId = versionId; + this.listenerHolder = new WeakListenerHolder(); + if (versionId == null) { + enableSpecificSettings.set(true); + listenerHolder.add(FXUtils.onWeakChangeAndOperate(profile.selectedVersionProperty(), this.selectedVersion::setValue)); + } + + VersionSetting versionSetting = profile.getVersionSetting(versionId); + + modpack.set(versionId != null && profile.getRepository().isModpack(versionId)); + + // unbind data fields + if (lastVersionSetting != null) { + FXUtils.unbind(txtWidth, lastVersionSetting.widthProperty()); + FXUtils.unbind(txtHeight, lastVersionSetting.heightProperty()); + maxMemory.unbindBidirectional(lastVersionSetting.maxMemoryProperty()); + FXUtils.unbind(txtJVMArgs, lastVersionSetting.javaArgsProperty()); + FXUtils.unbind(txtGameArgs, lastVersionSetting.minecraftArgsProperty()); + FXUtils.unbind(txtMetaspace, lastVersionSetting.permSizeProperty()); + FXUtils.unbind(txtServerIP, lastVersionSetting.serverIpProperty()); + FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty()); + FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty()); + FXUtils.unbindBoolean(isolateWorkingDirSwitch, lastVersionSetting.isolateGameDirProperty()); + FXUtils.unbindBoolean(noGameCheckSwitch, lastVersionSetting.notCheckGameProperty()); + FXUtils.unbindBoolean(noJVMCheckSwitch, lastVersionSetting.notCheckJVMProperty()); + FXUtils.unbindBoolean(beGestureSwitch, lastVersionSetting.beGestureProperty()); + FXUtils.unbindSelection(javaSpinner, lastVersionSetting.javaProperty()); + FXUtils.unbindSelection(processPrioritySpinner, lastVersionSetting.processPriorityProperty()); + FXUtils.unbindSelection(rendererSpinner, lastVersionSetting.rendererProperty()); + + lastVersionSetting.usesGlobalProperty().removeListener(specificSettingsListener); + } + + // bind new data fields + FXUtils.bindInt(txtWidth, versionSetting.widthProperty()); + FXUtils.bindInt(txtHeight, versionSetting.heightProperty()); + maxMemory.bindBidirectional(versionSetting.maxMemoryProperty()); + + FXUtils.bindString(txtJVMArgs, versionSetting.javaArgsProperty()); + FXUtils.bindString(txtGameArgs, versionSetting.minecraftArgsProperty()); + FXUtils.bindString(txtMetaspace, versionSetting.permSizeProperty()); + FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty()); + FXUtils.bindBoolean(chkAutoAllocate, versionSetting.autoMemoryProperty()); + FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty()); + FXUtils.bindBoolean(isolateWorkingDirSwitch, versionSetting.isolateGameDirProperty()); + FXUtils.bindBoolean(noGameCheckSwitch, versionSetting.notCheckGameProperty()); + FXUtils.bindBoolean(noJVMCheckSwitch, versionSetting.notCheckJVMProperty()); + FXUtils.bindBoolean(beGestureSwitch, versionSetting.beGestureProperty()); + FXUtils.bindSelection(javaSpinner, versionSetting.javaProperty()); + FXUtils.bindSelection(processPrioritySpinner, versionSetting.processPriorityProperty()); + FXUtils.bindSelection(rendererSpinner, versionSetting.rendererProperty()); + + versionSetting.usesGlobalProperty().addListener(specificSettingsListener); + if (versionId != null) + enableSpecificSettings.set(!versionSetting.isUsesGlobal()); + + lastVersionSetting = versionSetting; + + loadIcon(); + } + + private void onExploreIcon() { + if (versionId == null) + return; + + SelectIconDialog dialog = new SelectIconDialog(getContext()); + dialog.show(); + } + + private void onDeleteIcon() { + if (versionId == null) + return; + + File iconFile = profile.getRepository().getVersionIconFile(versionId); + if (iconFile.exists()) + iconFile.delete(); + loadIcon(); + } + + private void loadIcon() { + if (versionId == null) { + return; + } + + iconView.setImageDrawable(profile.getRepository().getVersionIconImage(versionId)); } } diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/setting/SettingPageManager.java b/FCL/src/main/java/com/tungsten/fcl/ui/setting/SettingPageManager.java index 10406272..d3267053 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/setting/SettingPageManager.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/setting/SettingPageManager.java @@ -3,6 +3,7 @@ package com.tungsten.fcl.ui.setting; import android.content.Context; import com.tungsten.fcl.R; +import com.tungsten.fcl.setting.Profiles; import com.tungsten.fcl.ui.PageManager; import com.tungsten.fcl.ui.UIListener; import com.tungsten.fcl.ui.manage.VersionSettingPage; @@ -47,6 +48,8 @@ public class SettingPageManager extends PageManager { communityPage = new CommunityPage(getContext(), PAGE_ID_SETTING_COMMUNITY, getParent(), R.layout.page_community); aboutPage = new AboutPage(getContext(), PAGE_ID_SETTING_ABOUT, getParent(), R.layout.page_about); + versionSettingPage.loadVersion(Profiles.getSelectedProfile(), null); + if (listener != null) { listener.onLoad(); } diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/version/Versions.java b/FCL/src/main/java/com/tungsten/fcl/ui/version/Versions.java index 4d496654..d8398412 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/version/Versions.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/version/Versions.java @@ -73,7 +73,7 @@ public class Versions { */ public static void deleteVersion(Context context, Profile profile, String version) { - boolean isIndependent = profile.getVersionSetting(version).getGameDirType() == GameDirectoryType.VERSION_FOLDER; + boolean isIndependent = profile.getVersionSetting(version).isIsolateGameDir(); String message = isIndependent ? String.format(context.getString(R.string.version_manage_remove_confirm_independent), version) : String.format(context.getString(R.string.version_manage_remove_confirm), version); FCLAlertDialog.Builder builder = new FCLAlertDialog.Builder(context); diff --git a/FCL/src/main/java/com/tungsten/fcl/util/FXUtils.java b/FCL/src/main/java/com/tungsten/fcl/util/FXUtils.java index 9d740abd..eba60f8e 100644 --- a/FCL/src/main/java/com/tungsten/fcl/util/FXUtils.java +++ b/FCL/src/main/java/com/tungsten/fcl/util/FXUtils.java @@ -3,10 +3,19 @@ package com.tungsten.fcl.util; import com.tungsten.fclcore.fakefx.beans.InvalidationListener; import com.tungsten.fclcore.fakefx.beans.Observable; import com.tungsten.fclcore.fakefx.beans.WeakInvalidationListener; +import com.tungsten.fclcore.fakefx.beans.property.Property; import com.tungsten.fclcore.fakefx.beans.value.ChangeListener; import com.tungsten.fclcore.fakefx.beans.value.ObservableValue; import com.tungsten.fclcore.fakefx.beans.value.WeakChangeListener; +import com.tungsten.fclcore.fakefx.util.StringConverter; +import com.tungsten.fclcore.util.fakefx.SafeStringConverter; +import com.tungsten.fcllibrary.component.view.FCLCheckBox; +import com.tungsten.fcllibrary.component.view.FCLEditText; +import com.tungsten.fcllibrary.component.view.FCLSpinner; +import com.tungsten.fcllibrary.component.view.FCLSwitch; +import java.lang.ref.WeakReference; +import java.util.Objects; import java.util.function.Consumer; public final class FXUtils { @@ -45,4 +54,92 @@ public final class FXUtils { return originalListener; } + public static void bind(FCLEditText editText, Property property, StringConverter converter) { + editText.setText(converter == null ? (String) property.getValue() : converter.toString(property.getValue())); + EditTextBindingListener listener = new EditTextBindingListener<>(editText, property, converter); + editText.stringProperty().addListener(listener); + property.addListener(listener); + } + + public static void bindInt(FCLEditText textField, Property property) { + bind(textField, property, SafeStringConverter.fromInteger()); + } + + public static void bindString(FCLEditText editText, Property property) { + bind(editText, property, null); + } + + public static void unbind(FCLEditText editText, Property property) { + EditTextBindingListener listener = new EditTextBindingListener<>(editText, property, null); + editText.stringProperty().removeListener(listener); + property.removeListener(listener); + } + + private static final class EditTextBindingListener implements InvalidationListener { + private final int hashCode; + private final WeakReference editTextRef; + private final WeakReference> propertyRef; + private final StringConverter converter; + + EditTextBindingListener(FCLEditText editText, Property property, StringConverter converter) { + this.editTextRef = new WeakReference<>(editText); + this.propertyRef = new WeakReference<>(property); + this.converter = converter; + this.hashCode = System.identityHashCode(editText) ^ System.identityHashCode(property); + } + + @Override + public void invalidated(Observable observable) { // On property change + FCLEditText editText = editTextRef.get(); + Property property = this.propertyRef.get(); + + if (editText != null && property != null) { + T value = property.getValue(); + editText.setText(converter == null ? (String) value : converter.toString(value)); + } + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof EditTextBindingListener)) + return false; + EditTextBindingListener other = (EditTextBindingListener) obj; + return this.hashCode == other.hashCode + && this.editTextRef.get() == other.editTextRef.get() + && this.propertyRef.get() == other.propertyRef.get(); + } + } + + public static void bindBoolean(FCLSwitch fclSwitch, Property property) { + fclSwitch.addCheckedChangeListener(); + fclSwitch.checkProperty().bindBidirectional(property); + } + + public static void unbindBoolean(FCLSwitch fclSwitch, Property property) { + fclSwitch.checkProperty().unbindBidirectional(property); + } + + public static void bindBoolean(FCLCheckBox checkBox, Property property) { + checkBox.addCheckedChangeListener(); + checkBox.checkProperty().bindBidirectional(property); + } + + public static void unbindBoolean(FCLCheckBox checkBox, Property property) { + checkBox.checkProperty().unbindBidirectional(property); + } + + public static void bindSelection(FCLSpinner spinner, Property property) { + spinner.addSelectListener(); + spinner.selectedItemProperty().bindBidirectional(property); + } + + public static void unbindSelection(FCLSpinner spinner, Property property) { + spinner.selectedItemProperty().unbindBidirectional(property); + } + } diff --git a/FCL/src/main/res/layout/item_remote_version.xml b/FCL/src/main/res/layout/item_remote_version.xml index a755a1fd..3d4adc0e 100644 --- a/FCL/src/main/res/layout/item_remote_version.xml +++ b/FCL/src/main/res/layout/item_remote_version.xml @@ -57,6 +57,7 @@ android:id="@+id/tag" android:layout_gravity="center" android:singleLine="true" + app:auto_text_background_tint="true" app:auto_text_tint="true"/> diff --git a/FCL/src/main/res/layout/item_spinner.xml b/FCL/src/main/res/layout/item_spinner.xml new file mode 100644 index 00000000..ce6c934a --- /dev/null +++ b/FCL/src/main/res/layout/item_spinner.xml @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/FCL/src/main/res/layout/item_spinner_dropdown.xml b/FCL/src/main/res/layout/item_spinner_dropdown.xml new file mode 100644 index 00000000..2f600aa2 --- /dev/null +++ b/FCL/src/main/res/layout/item_spinner_dropdown.xml @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/FCL/src/main/res/layout/page_version_setting.xml b/FCL/src/main/res/layout/page_version_setting.xml index 7938ea2f..3f23c83c 100644 --- a/FCL/src/main/res/layout/page_version_setting.xml +++ b/FCL/src/main/res/layout/page_version_setting.xml @@ -26,7 +26,7 @@ android:orientation="vertical"> - - + + + + @@ -119,7 +129,7 @@ android:orientation="vertical"> - - - 游戏参数 默认 进程优先级 - - - + 低(节省游戏占用资源) + 中(平衡) + 高(优先保障游戏进程) 服务器地址 启动游戏后自动加入对应服务器 diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java index b5a15324..985f9621 100644 --- a/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java @@ -28,7 +28,6 @@ public class LaunchOptions implements Serializable { private Integer height; private boolean fullscreen; private String serverIp; - private boolean noGeneratedJVMArgs; private ProcessPriority processPriority = ProcessPriority.NORMAL; private FCLConfig.Renderer renderer; @@ -141,13 +140,6 @@ public class LaunchOptions implements Serializable { return serverIp; } - /** - * Prevent game launcher from generating default JVM arguments like max memory. - */ - public boolean isNoGeneratedJVMArgs() { - return noGeneratedJVMArgs; - } - /** * Process priority */ @@ -276,13 +268,6 @@ public class LaunchOptions implements Serializable { return options.serverIp; } - /** - * Prevent game launcher from generating default JVM arguments like max memory. - */ - public boolean isNoGeneratedJVMArgs() { - return options.noGeneratedJVMArgs; - } - /** * Process priority */ @@ -375,11 +360,6 @@ public class LaunchOptions implements Serializable { return this; } - public Builder setNoGeneratedJVMArgs(boolean noGeneratedJVMArgs) { - options.noGeneratedJVMArgs = noGeneratedJVMArgs; - return this; - } - public Builder setProcessPriority(@NotNull ProcessPriority processPriority) { options.processPriority = processPriority; return this; diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/launch/DefaultLauncher.java b/FCLCore/src/main/java/com/tungsten/fclcore/launch/DefaultLauncher.java index caa5715d..18e1f4b2 100644 --- a/FCLCore/src/main/java/com/tungsten/fclcore/launch/DefaultLauncher.java +++ b/FCLCore/src/main/java/com/tungsten/fclcore/launch/DefaultLauncher.java @@ -55,57 +55,57 @@ public class DefaultLauncher extends Launcher { res.addAllWithoutParsing(options.getJavaArguments()); // JVM Args - if (!options.isNoGeneratedJVMArgs()) { - appendJvmArgs(res); + appendJvmArgs(res); - res.addDefault("-Dminecraft.client.jar=", repository.getVersionJar(version).toString()); + res.addDefault("-Dminecraft.client.jar=", repository.getVersionJar(version).toString()); - // Using G1GC with its settings by default - if (options.getJava().getVersion() >= 8) { - boolean addG1Args = true; - for (String javaArg : options.getJavaArguments()) { - if ("-XX:-UseG1GC".equals(javaArg) || (javaArg.startsWith("-XX:+Use") && javaArg.endsWith("GC"))) { - addG1Args = false; - break; - } - } - if (addG1Args) { - res.addUnstableDefault("UnlockExperimentalVMOptions", true); - res.addUnstableDefault("UseG1GC", true); - res.addUnstableDefault("G1NewSizePercent", "20"); - res.addUnstableDefault("G1ReservePercent", "20"); - res.addUnstableDefault("MaxGCPauseMillis", "50"); - res.addUnstableDefault("G1HeapRegionSize", "16m"); + // Using G1GC with its settings by default + if (options.getJava().getVersion() >= 8) { + boolean addG1Args = true; + for (String javaArg : options.getJavaArguments()) { + if ("-XX:-UseG1GC".equals(javaArg) || (javaArg.startsWith("-XX:+Use") && javaArg.endsWith("GC"))) { + addG1Args = false; + break; } } - - if (options.getMetaspace() != null && options.getMetaspace() > 0) - res.addDefault("-XX:MetaspaceSize=", options.getMetaspace() + "m"); - - res.addUnstableDefault("UseAdaptiveSizePolicy", false); - res.addUnstableDefault("OmitStackTraceInFastThrow", false); - res.addUnstableDefault("DontCompileHugeMethods", false); - res.addDefault("-Xmn", "128m"); - - // As 32-bit JVM allocate 320KB for stack by default rather than 64-bit version allocating 1MB, - // causing Minecraft 1.13 crashed accounting for java.lang.StackOverflowError. - if (Architecture.is32BitsDevice()) { - res.addDefault("-Xss", "1m"); + if (addG1Args) { + res.addUnstableDefault("UnlockExperimentalVMOptions", true); + res.addUnstableDefault("UseG1GC", true); + res.addUnstableDefault("G1NewSizePercent", "20"); + res.addUnstableDefault("G1ReservePercent", "20"); + res.addUnstableDefault("MaxGCPauseMillis", "50"); + res.addUnstableDefault("G1HeapRegionSize", "16m"); } - - if (options.getMaxMemory() != null && options.getMaxMemory() > 0) - res.addDefault("-Xmx", options.getMaxMemory() + "m"); - - if (options.getMinMemory() != null && options.getMinMemory() > 0) - res.addDefault("-Xms", options.getMinMemory() + "m"); - - res.addDefault("-Dfml.ignoreInvalidMinecraftCertificates=", "true"); - res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true"); -// res.addDefault("-Dorg.lwjgl.util.Debug=","true"); -// res.addDefault("-Dorg.lwjgl.util.DebugLoader=","true"); -// res.addDefault("-Dorg.lwjgl.util.DebugFunctions=","true"); } + if (options.getMetaspace() != null && options.getMetaspace() > 0) + res.addDefault("-XX:MetaspaceSize=", options.getMetaspace() + "m"); + + res.addUnstableDefault("UseAdaptiveSizePolicy", false); + res.addUnstableDefault("OmitStackTraceInFastThrow", false); + res.addUnstableDefault("DontCompileHugeMethods", false); + res.addDefault("-Xmn", "128m"); + + // As 32-bit JVM allocate 320KB for stack by default rather than 64-bit version allocating 1MB, + // causing Minecraft 1.13 crashed accounting for java.lang.StackOverflowError. + if (Architecture.is32BitsDevice()) { + res.addDefault("-Xss", "1m"); + } + + if (options.getMaxMemory() != null && options.getMaxMemory() > 0) + res.addDefault("-Xmx", options.getMaxMemory() + "m"); + + if (options.getMinMemory() != null && options.getMinMemory() > 0) + res.addDefault("-Xms", options.getMinMemory() + "m"); + + res.addDefault("-Dfml.ignoreInvalidMinecraftCertificates=", "true"); + res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true"); + + // Enable LWJGL debug mode +// res.addDefault("-Dorg.lwjgl.util.Debug=","true"); +// res.addDefault("-Dorg.lwjgl.util.DebugLoader=","true"); +// res.addDefault("-Dorg.lwjgl.util.DebugFunctions=","true"); + // Fix RCE vulnerability of log4j2 res.addDefault("-Djava.rmi.server.useCodebaseOnly=", "true"); res.addDefault("-Dcom.sun.jndi.rmi.object.trustURLCodebase=", "false"); diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/fakefx/SafeStringConverter.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/fakefx/SafeStringConverter.java new file mode 100644 index 00000000..52a7d9fe --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/fakefx/SafeStringConverter.java @@ -0,0 +1,103 @@ +package com.tungsten.fclcore.util.fakefx; + +import com.tungsten.fclcore.fakefx.util.StringConverter; +import com.tungsten.fclcore.util.function.ExceptionalFunction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class SafeStringConverter extends StringConverter { + + public static SafeStringConverter fromInteger() { + return new SafeStringConverter(Integer::parseInt, NumberFormatException.class) + .fallbackTo(0); + } + + public static SafeStringConverter fromDouble() { + return new SafeStringConverter(Double::parseDouble, NumberFormatException.class) + .fallbackTo(0.0); + } + + public static SafeStringConverter fromFiniteDouble() { + return new SafeStringConverter(Double::parseDouble, NumberFormatException.class) + .restrict(Double::isFinite) + .fallbackTo(0.0); + } + + private ExceptionalFunction converter; + private Class malformedExceptionClass; + private S fallbackValue = null; + private List> restrictions = new ArrayList<>(); + + public SafeStringConverter(ExceptionalFunction converter, Class malformedExceptionClass) { + this.converter = converter; + this.malformedExceptionClass = malformedExceptionClass; + } + + @Override + public String toString(T object) { + return object == null ? "" : object.toString(); + } + + @Override + public S fromString(String string) { + return tryParse(string).orElse(fallbackValue); + } + + private Optional tryParse(String string) { + if (string == null) { + return Optional.empty(); + } + + S converted; + try { + converted = converter.apply(string); + } catch (Exception e) { + if (malformedExceptionClass.isInstance(e)) { + return Optional.empty(); + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else { + throw new RuntimeException(e); + } + } + + if (!filter(converted)) { + return Optional.empty(); + } + + return Optional.of(converted); + } + + protected boolean filter(S value) { + for (Predicate restriction : restrictions) { + if (!restriction.test(value)) { + return false; + } + } + return true; + } + + public SafeStringConverter fallbackTo(S fallbackValue) { + this.fallbackValue = fallbackValue; + return this; + } + + public SafeStringConverter restrict(Predicate condition) { + this.restrictions.add(condition); + return this; + } + + public Predicate asPredicate() { + return string -> tryParse(string).isPresent(); + } + + public SafeStringConverter asPredicate(Consumer> consumer) { + consumer.accept(asPredicate()); + return this; + } +} \ No newline at end of file diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckBox.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckBox.java index 5d35ea1f..19ff1ae6 100644 --- a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckBox.java +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckBox.java @@ -21,6 +21,7 @@ import com.tungsten.fcllibrary.component.theme.ThemeEngine; public class FCLCheckBox extends AppCompatCheckBox { private boolean autoTint; + private boolean fromUserOrSystem = false; private BooleanProperty visibilityProperty; private BooleanProperty checkProperty; private BooleanProperty disableProperty; @@ -59,6 +60,13 @@ public class FCLCheckBox extends AppCompatCheckBox { } }; + public void addCheckedChangeListener() { + setOnCheckedChangeListener((compoundButton, b) -> { + fromUserOrSystem = true; + checkProperty().set(b); + }); + } + public FCLCheckBox(@NonNull Context context) { super(context); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); @@ -134,8 +142,11 @@ public class FCLCheckBox extends AppCompatCheckBox { public void invalidated() { Schedulers.androidUIThread().execute(() -> { - boolean isCheck = get(); - setChecked(isCheck); + if (!fromUserOrSystem) { + boolean isCheck = get(); + setChecked(isCheck); + } + fromUserOrSystem = false; }); } diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckedTextView.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckedTextView.java new file mode 100644 index 00000000..b2bf6149 --- /dev/null +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLCheckedTextView.java @@ -0,0 +1,85 @@ +package com.tungsten.fcllibrary.component.view; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tungsten.fclcore.fakefx.beans.property.IntegerProperty; +import com.tungsten.fclcore.fakefx.beans.property.IntegerPropertyBase; +import com.tungsten.fcllibrary.R; +import com.tungsten.fcllibrary.component.theme.ThemeEngine; + +public class FCLCheckedTextView extends androidx.appcompat.widget.AppCompatCheckedTextView { + + private boolean autoTint; + private boolean autoBackgroundTint; + + private final IntegerProperty theme = new IntegerPropertyBase() { + + @Override + protected void invalidated() { + get(); + if (autoTint) { + setTextColor(ThemeEngine.getInstance().getTheme().getAutoTint()); + } + if (autoBackgroundTint) { + setBackgroundTintList(new ColorStateList(new int[][] { { } }, new int[]{ ThemeEngine.getInstance().getTheme().getColor() })); + } + } + + @Override + public Object getBean() { + return this; + } + + @Override + public String getName() { + return "theme"; + } + }; + + public FCLCheckedTextView(@NonNull Context context) { + super(context); + autoTint = false; + autoBackgroundTint = false; + theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); + } + + public FCLCheckedTextView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLCheckedTextView); + autoTint = typedArray.getBoolean(R.styleable.FCLCheckedTextView_auto_checked_text_tint, false); + autoBackgroundTint = typedArray.getBoolean(R.styleable.FCLCheckedTextView_auto_checked_text_background_tint, false); + typedArray.recycle(); + theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); + } + + public FCLCheckedTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLCheckedTextView); + autoTint = typedArray.getBoolean(R.styleable.FCLCheckedTextView_auto_checked_text_tint, false); + autoBackgroundTint = typedArray.getBoolean(R.styleable.FCLCheckedTextView_auto_checked_text_background_tint, false); + typedArray.recycle(); + theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); + } + + public void setAutoTint(boolean autoTint) { + this.autoTint = autoTint; + } + + public boolean isAutoTint() { + return autoTint; + } + + public void setAutoBackgroundTint(boolean autoBackgroundTint) { + this.autoBackgroundTint = autoBackgroundTint; + } + + public boolean isAutoBackgroundTint() { + return autoBackgroundTint; + } +} diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLEditText.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLEditText.java index 4067487d..98059f61 100644 --- a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLEditText.java +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLEditText.java @@ -5,6 +5,8 @@ import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Build; +import android.text.Editable; +import android.text.TextWatcher; import android.util.AttributeSet; import androidx.annotation.NonNull; @@ -15,6 +17,9 @@ import com.tungsten.fclcore.fakefx.beans.property.BooleanProperty; import com.tungsten.fclcore.fakefx.beans.property.BooleanPropertyBase; import com.tungsten.fclcore.fakefx.beans.property.IntegerProperty; import com.tungsten.fclcore.fakefx.beans.property.IntegerPropertyBase; +import com.tungsten.fclcore.fakefx.beans.property.ReadOnlyBooleanProperty; +import com.tungsten.fclcore.fakefx.beans.property.StringProperty; +import com.tungsten.fclcore.fakefx.beans.property.StringPropertyBase; import com.tungsten.fclcore.task.Schedulers; import com.tungsten.fcllibrary.R; import com.tungsten.fcllibrary.component.theme.ThemeEngine; @@ -22,8 +27,40 @@ import com.tungsten.fcllibrary.component.theme.ThemeEngine; public class FCLEditText extends AppCompatEditText { private boolean autoTint; + private boolean fromUserOrSystem = false; private BooleanProperty visibilityProperty; private BooleanProperty disableProperty; + private BooleanProperty focusedProperty; + private StringProperty stringProperty; + + private final Thread focusListener = new Thread(() -> { + if (focusedProperty == null) { + focusedProperty = new BooleanPropertyBase() { + + public void invalidated() { + + } + + public Object getBean() { + return this; + } + + public String getName() { + return "focused"; + } + }; + } + while (true) { + focusedProperty.set(isFocused()); + } + }); + + public void runFocusListener() { + Schedulers.androidUIThread().execute(() -> { + focusListener.setPriority(Thread.MIN_PRIORITY); + focusListener.start(); + }); + } private final IntegerProperty theme = new IntegerPropertyBase() { @@ -62,9 +99,30 @@ public class FCLEditText extends AppCompatEditText { } }; + public void addTextWatcher() { + addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void afterTextChanged(Editable editable) { + fromUserOrSystem = true; + stringProperty().set(getText().toString()); + } + }); + } + public FCLEditText(@NonNull Context context) { super(context); autoTint = false; + addTextWatcher(); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -73,6 +131,7 @@ public class FCLEditText extends AppCompatEditText { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLEditText); autoTint = typedArray.getBoolean(R.styleable.FCLEditText_auto_edit_tint, false); typedArray.recycle(); + addTextWatcher(); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -81,6 +140,7 @@ public class FCLEditText extends AppCompatEditText { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLEditText); autoTint = typedArray.getBoolean(R.styleable.FCLEditText_auto_edit_tint, false); typedArray.recycle(); + addTextWatcher(); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -155,4 +215,64 @@ public class FCLEditText extends AppCompatEditText { return disableProperty; } + + public final boolean getFocusedValue() { + return focusedProperty == null || focusedProperty.get(); + } + + public final ReadOnlyBooleanProperty focusedProperty() { + if (focusedProperty == null) { + focusedProperty = new BooleanPropertyBase() { + + public void invalidated() { + + } + + public Object getBean() { + return this; + } + + public String getName() { + return "focused"; + } + }; + } + + return focusedProperty; + } + + public final void setStringValue(String string) { + stringProperty().set(string); + } + + public final String getStringValue() { + return stringProperty == null ? null : stringProperty.get(); + } + + public final StringProperty stringProperty() { + if (stringProperty == null) { + stringProperty = new StringPropertyBase() { + + public void invalidated() { + Schedulers.androidUIThread().execute(() -> { + if (!fromUserOrSystem) { + String s = get(); + setText(s); + } + fromUserOrSystem = false; + }); + } + + public Object getBean() { + return this; + } + + public String getName() { + return "visibility"; + } + }; + } + + return stringProperty; + } } diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSpinner.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSpinner.java new file mode 100644 index 00000000..1df9caab --- /dev/null +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSpinner.java @@ -0,0 +1,104 @@ +package com.tungsten.fcllibrary.component.view; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatSpinner; + +import com.tungsten.fclcore.fakefx.beans.property.ObjectProperty; +import com.tungsten.fclcore.fakefx.beans.property.ObjectPropertyBase; +import com.tungsten.fclcore.task.Schedulers; + +import java.util.ArrayList; + +public class FCLSpinner extends AppCompatSpinner { + + private boolean fromUserOrSystem = false; + private ArrayList dataList; + private ObjectProperty selectedItemProperty; + + public void addSelectListener() { + setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if (dataList != null && dataList.size() > i) { + fromUserOrSystem = true; + selectedItemProperty().set(dataList.get(i)); + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + } + + public FCLSpinner(@NonNull Context context) { + super(context); + } + + public FCLSpinner(@NonNull Context context, int mode) { + super(context, mode); + } + + public FCLSpinner(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public FCLSpinner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public FCLSpinner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int mode) { + super(context, attrs, defStyleAttr, mode); + } + + public FCLSpinner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int mode, Resources.Theme popupTheme) { + super(context, attrs, defStyleAttr, mode, popupTheme); + } + + public void setDataList(ArrayList dataList) { + this.dataList = dataList; + } + + public ArrayList getDataList() { + return dataList; + } + + public final Object getSelectedItemValue() { + return selectedItemProperty == null ? null : selectedItemProperty.get(); + } + + public final ObjectProperty selectedItemProperty() { + if (selectedItemProperty == null) { + selectedItemProperty = new ObjectPropertyBase() { + + public void invalidated() { + Schedulers.androidUIThread().execute(() -> { + if (!fromUserOrSystem) { + T data = get(); + setSelection(dataList.indexOf(data)); + } + fromUserOrSystem = false; + }); + } + + public Object getBean() { + return this; + } + + public String getName() { + return "selectedItem"; + } + }; + } + + return selectedItemProperty; + } +} diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSwitch.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSwitch.java index cb63182c..3920d989 100644 --- a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSwitch.java +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLSwitch.java @@ -18,6 +18,7 @@ import com.tungsten.fcllibrary.component.theme.ThemeEngine; public class FCLSwitch extends SwitchCompat { + private boolean fromUserOrSystem = false; private BooleanProperty visibilityProperty; private BooleanProperty checkProperty; private BooleanProperty disableProperty; @@ -58,6 +59,13 @@ public class FCLSwitch extends SwitchCompat { } }; + public void addCheckedChangeListener() { + setOnCheckedChangeListener((compoundButton, b) -> { + fromUserOrSystem = true; + checkProperty().set(b); + }); + } + public FCLSwitch(@NonNull Context context) { super(context); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); @@ -119,8 +127,11 @@ public class FCLSwitch extends SwitchCompat { public void invalidated() { Schedulers.androidUIThread().execute(() -> { - boolean isCheck = get(); - setChecked(isCheck); + if (!fromUserOrSystem) { + boolean isCheck = get(); + setChecked(isCheck); + } + fromUserOrSystem = false; }); } diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLTextView.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLTextView.java index 9f9172a9..9c3f4497 100644 --- a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLTextView.java +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLTextView.java @@ -1,6 +1,7 @@ package com.tungsten.fcllibrary.component.view; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; import android.util.AttributeSet; @@ -22,6 +23,7 @@ import com.tungsten.fcllibrary.component.theme.ThemeEngine; public class FCLTextView extends AppCompatTextView { private boolean autoTint; + private boolean autoBackgroundTint; private StringProperty string; private BooleanProperty visibilityProperty; @@ -33,6 +35,9 @@ public class FCLTextView extends AppCompatTextView { if (autoTint) { setTextColor(ThemeEngine.getInstance().getTheme().getAutoTint()); } + if (autoBackgroundTint) { + setBackgroundTintList(new ColorStateList(new int[][] { { } }, new int[]{ ThemeEngine.getInstance().getTheme().getColor() })); + } } @Override @@ -48,6 +53,8 @@ public class FCLTextView extends AppCompatTextView { public FCLTextView(@NonNull Context context) { super(context); + autoTint = false; + autoBackgroundTint = false; theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -55,6 +62,7 @@ public class FCLTextView extends AppCompatTextView { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLTextView); autoTint = typedArray.getBoolean(R.styleable.FCLTextView_auto_text_tint, false); + autoBackgroundTint = typedArray.getBoolean(R.styleable.FCLTextView_auto_text_background_tint, false); typedArray.recycle(); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -63,6 +71,7 @@ public class FCLTextView extends AppCompatTextView { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FCLTextView); autoTint = typedArray.getBoolean(R.styleable.FCLTextView_auto_text_tint, false); + autoBackgroundTint = typedArray.getBoolean(R.styleable.FCLTextView_auto_text_background_tint, false); typedArray.recycle(); theme.bind(ThemeEngine.getInstance().getTheme().colorProperty()); } @@ -87,6 +96,14 @@ public class FCLTextView extends AppCompatTextView { return autoTint; } + public void setAutoBackgroundTint(boolean autoBackgroundTint) { + this.autoBackgroundTint = autoBackgroundTint; + } + + public boolean isAutoBackgroundTint() { + return autoBackgroundTint; + } + public final void setString(String string) { stringProperty().set(string); } diff --git a/FCLLibrary/src/main/res/values/attrs.xml b/FCLLibrary/src/main/res/values/attrs.xml index 5dcde4b3..b0bf4f5f 100644 --- a/FCLLibrary/src/main/res/values/attrs.xml +++ b/FCLLibrary/src/main/res/values/attrs.xml @@ -13,8 +13,13 @@ + + + + +