bind/unbind setting page views

This commit is contained in:
Tungstend 2023-01-08 23:33:37 +08:00
parent 79ea64c696
commit 0e3505ff8a
24 changed files with 904 additions and 131 deletions

View File

@ -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()) {

View File

@ -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<Exception, ?> 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<Exception, ?> 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(""));

View File

@ -301,18 +301,35 @@ public final class VersionSetting implements Cloneable {
* 0 - .minecraft<br/>
* 1 - .minecraft/versions/&lt;version&gt;/<br/>
*/
private final ObjectProperty<GameDirectoryType> gameDirTypeProperty = new SimpleObjectProperty<>(this, "gameDirType", GameDirectoryType.ROOT_FOLDER);
private final BooleanProperty isolateGameDirProperty = new SimpleBooleanProperty(this, "isolateGameDir", false);
public ObjectProperty<GameDirectoryType> 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<ProcessPriority> processPriorityProperty = new SimpleObjectProperty<>(this, "processPriority", ProcessPriority.NORMAL);
@ -331,11 +348,11 @@ public final class VersionSetting implements Cloneable {
private final ObjectProperty<FCLConfig.Renderer> rendererProperty = new SimpleObjectProperty<>(this, "render", FCLConfig.Renderer.RENDERER_GL4ES);
public ObjectProperty<FCLConfig.Renderer> renderProperty() {
public ObjectProperty<FCLConfig.Renderer> 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;
}

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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<Integer> javaSpinner;
private FCLSpinner<ProcessPriority> processPrioritySpinner;
private FCLSpinner<FCLConfig.Renderer> 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<Integer> 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<ProcessPriority> processPriorityDataList = new ArrayList<>();
processPriorityDataList.add(ProcessPriority.LOW);
processPriorityDataList.add(ProcessPriority.NORMAL);
processPriorityDataList.add(ProcessPriority.HIGH);
processPrioritySpinner.setDataList(processPriorityDataList);
ArrayList<FCLConfig.Renderer> 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<String> javaVersionList = new ArrayList<>();
javaVersionList.add(getContext().getString(R.string.settings_game_java_version_auto));
javaVersionList.add("JRE 8");
javaVersionList.add("JRE 17");
ArrayAdapter<String> javaAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner, javaVersionList);
javaAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown);
javaSpinner.setAdapter(javaAdapter);
ArrayList<String> 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<String> processPriorityAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner, processPriorityList);
processPriorityAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown);
processPrioritySpinner.setAdapter(processPriorityAdapter);
ArrayList<String> 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<String> 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));
}
}

View File

@ -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();
}

View File

@ -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);

View File

@ -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 <T> void bind(FCLEditText editText, Property<T> property, StringConverter<T> converter) {
editText.setText(converter == null ? (String) property.getValue() : converter.toString(property.getValue()));
EditTextBindingListener<T> listener = new EditTextBindingListener<>(editText, property, converter);
editText.stringProperty().addListener(listener);
property.addListener(listener);
}
public static void bindInt(FCLEditText textField, Property<Number> property) {
bind(textField, property, SafeStringConverter.fromInteger());
}
public static void bindString(FCLEditText editText, Property<String> 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<T> implements InvalidationListener {
private final int hashCode;
private final WeakReference<FCLEditText> editTextRef;
private final WeakReference<Property<T>> propertyRef;
private final StringConverter<T> converter;
EditTextBindingListener(FCLEditText editText, Property<T> property, StringConverter<T> 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<T> 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<Boolean> property) {
fclSwitch.addCheckedChangeListener();
fclSwitch.checkProperty().bindBidirectional(property);
}
public static void unbindBoolean(FCLSwitch fclSwitch, Property<Boolean> property) {
fclSwitch.checkProperty().unbindBidirectional(property);
}
public static void bindBoolean(FCLCheckBox checkBox, Property<Boolean> property) {
checkBox.addCheckedChangeListener();
checkBox.checkProperty().bindBidirectional(property);
}
public static void unbindBoolean(FCLCheckBox checkBox, Property<Boolean> property) {
checkBox.checkProperty().unbindBidirectional(property);
}
public static <T> void bindSelection(FCLSpinner<T> spinner, Property<T> property) {
spinner.addSelectListener();
spinner.selectedItemProperty().bindBidirectional(property);
}
public static <T> void unbindSelection(FCLSpinner<T> spinner, Property<T> property) {
spinner.selectedItemProperty().unbindBidirectional(property);
}
}

View File

@ -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"/>
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<com.tungsten.fcllibrary.component.view.FCLTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fclTextView"
android:padding="8dp"
style="?android:attr/spinnerItemStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:ellipsize="marquee"
android:singleLine="true"
android:textAlignment="inherit"
android:background="@android:color/transparent"
app:auto_text_tint="true"
android:textSize="14sp" />

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<com.tungsten.fcllibrary.component.view.FCLCheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fclCheckedTextView"
style="?android:attr/spinnerDropDownItemStyle"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:textAlignment="inherit"
android:textSize="14sp"
android:background="@color/white"
app:auto_checked_text_tint="true"
app:auto_checked_text_background_tint="true"/>

View File

@ -26,7 +26,7 @@
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -62,7 +62,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -71,15 +71,7 @@
android:paddingTop="8dp"
android:paddingBottom="8dp">
<com.tungsten.fcllibrary.component.view.FCLImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/img_grass"
android:layout_gravity="center"
android:id="@+id/icon"/>
<com.tungsten.fcllibrary.component.view.FCLTextView
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:auto_text_tint="true"
@ -92,15 +84,33 @@
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tungsten.fcllibrary.component.view.FCLImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/img_grass"
android:layout_gravity="center"
android:id="@+id/icon"/>
<com.tungsten.fcllibrary.component.view.FCLImageButton
app:no_padding="true"
app:auto_tint="true"
android:layout_marginStart="15dp"
android:src="@drawable/ic_baseline_edit_24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/edit_icon"/>
<com.tungsten.fcllibrary.component.view.FCLImageButton
app:no_padding="true"
app:auto_tint="true"
android:layout_marginStart="15dp"
android:src="@drawable/ic_baseline_close_24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/delete_icon"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.tungsten.fcllibrary.component.view.FCLLinearLayout>
@ -119,7 +129,7 @@
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -141,7 +151,7 @@
android:layout_height="0dp"
android:layout_weight="1"/>
<androidx.appcompat.widget.AppCompatSpinner
<com.tungsten.fcllibrary.component.view.FCLSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -155,7 +165,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -293,7 +303,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -361,7 +371,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -383,7 +393,7 @@
android:layout_height="0dp"
android:layout_weight="1"/>
<androidx.appcompat.widget.AppCompatSpinner
<com.tungsten.fcllibrary.component.view.FCLSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -397,7 +407,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -439,7 +449,7 @@
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -478,7 +488,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -514,7 +524,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -536,7 +546,7 @@
android:layout_height="0dp"
android:layout_weight="1"/>
<androidx.appcompat.widget.AppCompatSpinner
<com.tungsten.fcllibrary.component.view.FCLSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -555,7 +565,7 @@
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -591,7 +601,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -628,7 +638,7 @@
tools:ignore="TooManyViews" />
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -665,7 +675,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -701,7 +711,7 @@
android:background="@android:color/darker_gray"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:minHeight="46dp"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"

View File

@ -172,9 +172,9 @@
<string name="settings_advanced_minecraft_arguments">游戏参数</string>
<string name="settings_advanced_minecraft_arguments_prompt">默认</string>
<string name="settings_advanced_process_priority">进程优先级</string>
<string name="settings_advanced_process_priority_low"></string>
<string name="settings_advanced_process_priority_normal"></string>
<string name="settings_advanced_process_priority_high"></string>
<string name="settings_advanced_process_priority_low">(节省游戏占用资源)</string>
<string name="settings_advanced_process_priority_normal">(平衡)</string>
<string name="settings_advanced_process_priority_high">(优先保障游戏进程)</string>
<string name="settings_advanced_server_ip">服务器地址</string>
<string name="settings_advanced_server_ip_prompt">启动游戏后自动加入对应服务器</string>

View File

@ -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;

View File

@ -55,7 +55,6 @@ public class DefaultLauncher extends Launcher {
res.addAllWithoutParsing(options.getJavaArguments());
// JVM Args
if (!options.isNoGeneratedJVMArgs()) {
appendJvmArgs(res);
res.addDefault("-Dminecraft.client.jar=", repository.getVersionJar(version).toString());
@ -101,10 +100,11 @@ public class DefaultLauncher extends Launcher {
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");

View File

@ -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<S extends T, T> extends StringConverter<T> {
public static SafeStringConverter<Integer, Number> fromInteger() {
return new SafeStringConverter<Integer, Number>(Integer::parseInt, NumberFormatException.class)
.fallbackTo(0);
}
public static SafeStringConverter<Double, Number> fromDouble() {
return new SafeStringConverter<Double, Number>(Double::parseDouble, NumberFormatException.class)
.fallbackTo(0.0);
}
public static SafeStringConverter<Double, Number> fromFiniteDouble() {
return new SafeStringConverter<Double, Number>(Double::parseDouble, NumberFormatException.class)
.restrict(Double::isFinite)
.fallbackTo(0.0);
}
private ExceptionalFunction<String, S, ?> converter;
private Class<?> malformedExceptionClass;
private S fallbackValue = null;
private List<Predicate<S>> restrictions = new ArrayList<>();
public <E extends Exception> SafeStringConverter(ExceptionalFunction<String, S, E> converter, Class<E> 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<S> 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<S> restriction : restrictions) {
if (!restriction.test(value)) {
return false;
}
}
return true;
}
public SafeStringConverter<S, T> fallbackTo(S fallbackValue) {
this.fallbackValue = fallbackValue;
return this;
}
public SafeStringConverter<S, T> restrict(Predicate<S> condition) {
this.restrictions.add(condition);
return this;
}
public Predicate<String> asPredicate() {
return string -> tryParse(string).isPresent();
}
public SafeStringConverter<S, T> asPredicate(Consumer<Predicate<String>> consumer) {
consumer.accept(asPredicate());
return this;
}
}

View File

@ -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(() -> {
if (!fromUserOrSystem) {
boolean isCheck = get();
setChecked(isCheck);
}
fromUserOrSystem = false;
});
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<T> extends AppCompatSpinner {
private boolean fromUserOrSystem = false;
private ArrayList<T> dataList;
private ObjectProperty<T> 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<T> dataList) {
this.dataList = dataList;
}
public ArrayList<T> getDataList() {
return dataList;
}
public final Object getSelectedItemValue() {
return selectedItemProperty == null ? null : selectedItemProperty.get();
}
public final ObjectProperty<T> selectedItemProperty() {
if (selectedItemProperty == null) {
selectedItemProperty = new ObjectPropertyBase<T>() {
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;
}
}

View File

@ -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(() -> {
if (!fromUserOrSystem) {
boolean isCheck = get();
setChecked(isCheck);
}
fromUserOrSystem = false;
});
}

View File

@ -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);
}

View File

@ -13,8 +13,13 @@
<attr name="auto_src_tint" format="boolean"/>
</declare-styleable>
<declare-styleable name="FCLTextView">
<attr name="auto_text_background_tint" format="boolean"/>
<attr name="auto_text_tint" format="boolean"/>
</declare-styleable>
<declare-styleable name="FCLCheckedTextView">
<attr name="auto_checked_text_background_tint" format="boolean"/>
<attr name="auto_checked_text_tint" format="boolean"/>
</declare-styleable>
<declare-styleable name="FCLEditText">
<attr name="auto_edit_tint" format="boolean"/>
</declare-styleable>