Settings Menu. Please help me find someone who can do this better

This commit is contained in:
Patrick
2026-06-21 00:48:38 +02:00
parent a80bffbaec
commit 36ab743b7d
5 changed files with 154 additions and 71 deletions
@@ -11,8 +11,12 @@ import icyllis.modernui.widget.TextView;
public abstract class BaseTabFragment extends Fragment {
protected static final int COLOR_CARD = 0xFF1A1A22;
protected static final int COLOR_ACCENT = 0xFF00E5FF;
protected static final int COLOR_CARD = 0xFF23232D;
protected static final int COLOR_ACCENT = 0xFF5CF392;
protected static final int COLOR_ACCENT_DIM = 0xFF3E8E5A;
protected static final int COLOR_TEXT_MAIN = 0xFFF0F0F0;
protected static final int COLOR_TEXT_SEC = 0xFF9EA3AF;
protected static final int COLOR_CARD_BORDER = 0x265CF392;
protected int dp(float dp) {
return (int) (dp * getContext().getResources().getDisplayMetrics().density);
@@ -22,7 +26,7 @@ public abstract class BaseTabFragment extends Fragment {
TextView tv = new TextView(getContext());
tv.setText(text);
tv.setTextSize(14);
tv.setTextColor(0xFFEEEEEE);
tv.setTextColor(COLOR_TEXT_MAIN);
var p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
p.topMargin = dp(12);
p.bottomMargin = dp(4);
@@ -37,20 +41,45 @@ public abstract class BaseTabFragment extends Fragment {
tv.setTextColor(COLOR_ACCENT);
var p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
p.topMargin = dp(24);
p.bottomMargin = dp(8);
p.bottomMargin = dp(10);
p.leftMargin = dp(4);
parent.addView(tv, p);
}
protected void addTabHeader(LinearLayout parent, String title, String description) {
TextView titleView = new TextView(getContext());
titleView.setText(title);
titleView.setTextSize(24);
titleView.setTextColor(0xFFFFFFFF);
titleView.setPadding(0, 0, 0, dp(2));
parent.addView(titleView);
if (description != null) {
TextView descView = new TextView(getContext());
descView.setText(description);
descView.setTextSize(13);
descView.setTextColor(COLOR_TEXT_SEC);
descView.setPadding(0, 0, 0, dp(16));
parent.addView(descView);
}
View divider = new View(getContext());
var divParams = new LinearLayout.LayoutParams(dp(40), dp(3));
divParams.bottomMargin = dp(16);
divider.setLayoutParams(divParams);
divider.setBackground(makeRoundedBg(COLOR_ACCENT, 2));
parent.addView(divider);
}
protected LinearLayout createSettingCard(String title, String description) {
LinearLayout card = new LinearLayout(getContext());
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
card.setPadding(dp(16), dp(12), dp(16), dp(12));
card.setBackground(makeRoundedBg(COLOR_CARD, 12));
card.setPadding(dp(18), dp(14), dp(18), dp(14));
card.setBackground(makeBorderedBg(COLOR_CARD, COLOR_CARD_BORDER, 10, 1));
var cardParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
cardParams.bottomMargin = dp(8);
cardParams.bottomMargin = dp(10);
card.setLayoutParams(cardParams);
LinearLayout textLayout = new LinearLayout(getContext());
@@ -60,15 +89,15 @@ public abstract class BaseTabFragment extends Fragment {
TextView titleView = new TextView(getContext());
titleView.setText(title);
titleView.setTextSize(15);
titleView.setTextColor(0xFFFFFFFF);
titleView.setTextSize(15.5f);
titleView.setTextColor(COLOR_TEXT_MAIN);
textLayout.addView(titleView);
if (description != null) {
TextView descView = new TextView(getContext());
descView.setText(description);
descView.setTextSize(12);
descView.setTextColor(0xFF888888);
descView.setTextSize(12.5f);
descView.setTextColor(COLOR_TEXT_SEC);
textLayout.addView(descView);
}
@@ -82,6 +111,14 @@ public abstract class BaseTabFragment extends Fragment {
return d;
}
protected ShapeDrawable makeBorderedBg(int color, int strokeColor, float radius, float strokeWidth) {
ShapeDrawable d = new ShapeDrawable();
d.setCornerRadius(dp(radius));
d.setColor(color);
d.setStroke(dp(strokeWidth), strokeColor);
return d;
}
@Override
public void onViewCreated(View view, icyllis.modernui.util.DataSet savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -21,33 +21,35 @@ public class DiscordTabFragment extends BaseTabFragment {
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(dp(24), dp(8), dp(24), dp(24));
layout.setPadding(dp(28), dp(24), dp(28), dp(24));
scrollView.addView(layout);
addSectionHeader(layout, "Discord Integration");
addTabHeader(layout, "Discord Rich Presence", "Connect your Minecraft activity to your Discord profile.");
addSwitchRow(layout, "Enable DiscordRPC", "Show your game status on Discord",
addSectionHeader(layout, "Core Integration");
addSwitchRow(layout, "Status Integration", "Toggle Discord Rich Presence globally.",
ConfigManager.getInstance().enableDiscordRPC, (v, checked) -> {
ConfigManager.getInstance().enableDiscordRPC = checked;
ConfigManager.save();
updateRPCDetailsEnabled(checked);
});
addSectionHeader(layout, "RPC Details");
addSectionHeader(layout, "Status Customization");
rpcDetailViews.add(addSwitchRow(layout, "Show Biome", "Display current biome in status",
rpcDetailViews.add(addSwitchRow(layout, "Location Tracking", "Broadcast your current biome.",
ConfigManager.getInstance().showBiomeInRPC, (v, checked) -> {
ConfigManager.getInstance().showBiomeInRPC = checked;
ConfigManager.save();
}));
rpcDetailViews.add(addSwitchRow(layout, "Show Held Item", "Display what you are holding",
rpcDetailViews.add(addSwitchRow(layout, "Equipment Status", "Show your currently held items.",
ConfigManager.getInstance().showHeldItemInRPC, (v, checked) -> {
ConfigManager.getInstance().showHeldItemInRPC = checked;
ConfigManager.save();
}));
rpcDetailViews.add(addSwitchRow(layout, "Show Health", "Display your current HP",
rpcDetailViews.add(addSwitchRow(layout, "Vitality Info", "Display your current health status.",
ConfigManager.getInstance().showHealthInRPC, (v, checked) -> {
ConfigManager.getInstance().showHealthInRPC = checked;
ConfigManager.save();
@@ -15,23 +15,27 @@ public class GeneralTabFragment extends BaseTabFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, DataSet savedInstanceData) {
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(dp(24), dp(8), dp(24), dp(24));
layout.setPadding(dp(28), dp(24), dp(28), dp(24));
addSectionHeader(layout, "Client Info");
layout.addView(createSettingCard("Minecraft Version", Minecraft.getInstance().getLaunchedVersion()));
addTabHeader(layout, "Client Settings", "Configure your client-side experience and mod data.");
addSectionHeader(layout, "Config Management");
addSectionHeader(layout, "Information");
layout.addView(createSettingCard("Version", "Running on Minecraft " + Minecraft.getInstance().getLaunchedVersion()));
LinearLayout reloadCard = createSettingCard("Reload Config", "Reset all settings to saved values");
addSectionHeader(layout, "Data Management");
LinearLayout reloadCard = createSettingCard("Reload configuration", "Discard unsaved changes and reload from disk.");
Button reloadBtn = new Button(getContext());
reloadBtn.setText("Reload");
reloadBtn.setPadding(dp(16), 0, dp(16), 0);
reloadBtn.setOnClickListener(v -> ConfigManager.load());
reloadCard.addView(reloadBtn);
layout.addView(reloadCard);
LinearLayout saveCard = createSettingCard("Save Config", "Persist current settings to disk");
LinearLayout saveCard = createSettingCard("Commit changes", "Save all current settings to the configuration file.");
Button saveBtn = new Button(getContext());
saveBtn.setText("Save");
saveBtn.setPadding(dp(16), 0, dp(16), 0);
saveBtn.setOnClickListener(v -> ConfigManager.save());
saveCard.addView(saveBtn);
layout.addView(saveCard);
@@ -18,28 +18,30 @@ public class InfoTabFragment extends BaseTabFragment {
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(dp(24), dp(8), dp(24), dp(24));
layout.setPadding(dp(28), dp(24), dp(28), dp(24));
scrollView.addView(layout);
addSectionHeader(layout, "Mod Versions");
addTabHeader(layout, "System Overview", "Detailed information about your environment and mod versions.");
addSectionHeader(layout, "Software Manifest");
String parrotVersion = FabricLoader.getInstance().getModContainer("parrotmod").orElseThrow().getMetadata().getVersion().getFriendlyString();
layout.addView(createSettingCard("Parrotmod", parrotVersion));
layout.addView(createSettingCard("ParrotMod", "Stable Build v" + parrotVersion));
String mcVersion = FabricLoader.getInstance().getModContainer("minecraft").orElseThrow().getMetadata().getVersion().getFriendlyString();
layout.addView(createSettingCard("Minecraft", Minecraft.getInstance().getLaunchedVersion() + " (" + mcVersion + ")"));
layout.addView(createSettingCard("Minecraft Engine", Minecraft.getInstance().getLaunchedVersion() + " (" + mcVersion + ")"));
String loaderVersion = FabricLoader.getInstance().getModContainer("fabricloader").orElseThrow().getMetadata().getVersion().getFriendlyString();
layout.addView(createSettingCard("Fabric Loader", loaderVersion));
layout.addView(createSettingCard("Fabric Loader", "Build " + loaderVersion));
addSectionHeader(layout, "System Info");
layout.addView(createSettingCard("Java", System.getProperty("java.version") + " (" + System.getProperty("os.arch") + ")"));
layout.addView(createSettingCard("OS", System.getProperty("os.name") + " (" + System.getProperty("os.version") + ")"));
addSectionHeader(layout, "Runtime Environment");
layout.addView(createSettingCard("Java Runtime", System.getProperty("java.version") + " [" + System.getProperty("os.arch") + "]"));
layout.addView(createSettingCard("Operating System", System.getProperty("os.name") + " [" + System.getProperty("os.version") + "]"));
long maxMemory = Runtime.getRuntime().maxMemory();
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
long usedMemory = totalMemory - freeMemory;
layout.addView(createSettingCard("Memory Usage", String.format("%dMB / %dMB", usedMemory / 1024 / 1024, maxMemory / 1024 / 1024)));
layout.addView(createSettingCard("Memory Allocation", String.format("%d MiB / %d MiB in use", usedMemory / 1024 / 1024, maxMemory / 1024 / 1024)));
return scrollView;
}
@@ -12,18 +12,20 @@ import net.fabricmc.loader.api.FabricLoader;
public class SettingsFragment extends Fragment {
private static final int COLOR_BACKGROUND = 0xFF0F0F13;
private static final int COLOR_SIDEBAR = 0xFF15151B;
private static final int COLOR_ACCENT = 0xFF00E5FF;
private static final int COLOR_TEXT_DIM = 0xFFAAAAAA;
private static final int COLOR_BACKGROUND = 0xFF18181F;
private static final int COLOR_SIDEBAR = 0xFF121217;
private static final int COLOR_ACCENT = 0xFF5CF392;
private static final int COLOR_TEXT_DIM = 0xFF9EA3AF;
private static final int COLOR_TAB_SELECTED = 0x125CF392;
private static final int COLOR_BORDER = 0x1AFFFFFF;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, DataSet savedInstanceState) {
FrameLayout root = new FrameLayout(getContext());
root.setLayoutParams(new FrameLayout.LayoutParams(dp(720), dp(480), Gravity.CENTER));
root.setLayoutParams(new FrameLayout.LayoutParams(dp(760), dp(520), Gravity.CENTER));
View bg = new View(getContext());
bg.setBackground(makeRoundedBg(COLOR_BACKGROUND, 16));
bg.setBackground(makeBorderedBg(COLOR_BACKGROUND, COLOR_BORDER, 14, 1.2f));
root.addView(bg);
LinearLayout layout = new LinearLayout(getContext());
@@ -31,26 +33,37 @@ public class SettingsFragment extends Fragment {
root.addView(layout);
FrameLayout sidebarContainer = new FrameLayout(getContext());
var sidebarParams = new LinearLayout.LayoutParams(dp(170), ViewGroup.LayoutParams.MATCH_PARENT);
var sidebarParams = new LinearLayout.LayoutParams(dp(190), ViewGroup.LayoutParams.MATCH_PARENT);
layout.addView(sidebarContainer, sidebarParams);
View sidebarBg = new View(getContext());
sidebarBg.setBackground(makeRoundedBg(COLOR_SIDEBAR, 16));
sidebarBg.setBackground(makeRoundedBg(COLOR_SIDEBAR, 14));
sidebarContainer.addView(sidebarBg);
LinearLayout sidebarContent = new LinearLayout(getContext());
sidebarContent.setOrientation(LinearLayout.VERTICAL);
sidebarContent.setPadding(0, dp(24), 0, dp(16));
sidebarContent.setPadding(0, dp(28), 0, dp(16));
sidebarContainer.addView(sidebarContent, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
View separator = new View(getContext());
var sepParams = new LinearLayout.LayoutParams(dp(1), ViewGroup.LayoutParams.MATCH_PARENT);
separator.setOutlineAmbientShadowColor(COLOR_BORDER);
layout.addView(separator, sepParams);
LinearLayout titleLayout = new LinearLayout(getContext());
titleLayout.setOrientation(LinearLayout.VERTICAL);
titleLayout.setGravity(Gravity.CENTER);
titleLayout.setPadding(0, 0, 0, dp(32));
TextView title = new TextView(getContext());
title.setText("PARROT");
title.setTextSize(22);
title.setGravity(Gravity.CENTER);
title.setTextColor(COLOR_ACCENT);
title.setPadding(dp(16), 0, dp(16), dp(32));
sidebarContent.addView(title);
titleLayout.addView(title);
sidebarContent.addView(titleLayout);
FrameLayout tabsContainer = new FrameLayout(getContext());
sidebarContent.addView(tabsContainer, new LinearLayout.LayoutParams(
@@ -58,8 +71,9 @@ public class SettingsFragment extends Fragment {
LinearLayout tabsLayout = new LinearLayout(getContext());
tabsLayout.setOrientation(LinearLayout.VERTICAL);
tabsLayout.setPadding(dp(12), 0, dp(12), 0);
tabsContainer.addView(tabsLayout, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
FrameLayout contentArea = new FrameLayout(getContext());
@@ -74,56 +88,72 @@ public class SettingsFragment extends Fragment {
new DiscordTabFragment()
};
Button[] tabButtons = new Button[tabNames.length];
View[] tabContainers = new View[tabNames.length];
View[] indicators = new View[tabNames.length];
TextView[] textViews = new TextView[tabNames.length];
for (int i = 0; i < tabNames.length; i++) {
final int index = i;
Button tab = new Button(getContext());
tab.setText(tabNames[index]);
tab.setTextSize(14);
tab.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
tab.setPadding(dp(32), 0, dp(16), 0);
tab.setBackground(null);
tab.setTextColor(COLOR_TEXT_DIM);
tab.setOnClickListener(v -> {
FrameLayout tabContainer = new FrameLayout(getContext());
View indicator = new View(getContext());
indicator.setBackground(makeRoundedBg(COLOR_ACCENT, 2));
indicator.setVisibility(View.INVISIBLE);
var indParams = new FrameLayout.LayoutParams(dp(3), dp(18), Gravity.START | Gravity.CENTER_VERTICAL);
tabContainer.addView(indicator, indParams);
TextView tabText = new TextView(getContext());
tabText.setText(tabNames[index]);
tabText.setTextSize(14);
tabText.setPadding(dp(24), 0, dp(16), 0);
tabText.setTextColor(COLOR_TEXT_DIM);
tabText.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
tabContainer.addView(tabText, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dp(40)));
tabContainer.setOnClickListener(v -> {
getChildFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.replace(contentArea.getId(), tabFragments[index])
.commit();
for (int j = 0; j < tabButtons.length; j++) {
for (int j = 0; j < tabNames.length; j++) {
boolean selected = (j == index);
tabButtons[j].setTextColor(selected ? 0xFFFFFFFF : COLOR_TEXT_DIM);
tabButtons[j].setBackground(selected ? makeRoundedBg(0x12FFFFFF, 8) : null);
textViews[j].setTextColor(selected ? 0xFFFFFFFF : COLOR_TEXT_DIM);
tabContainers[j].setBackground(selected ? makeRoundedBg(COLOR_TAB_SELECTED, 8) : null);
indicators[j].setVisibility(selected ? View.VISIBLE : View.INVISIBLE);
}
});
var tabParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(44));
tabParams.bottomMargin = dp(4);
tabsLayout.addView(tab, tabParams);
tabButtons[index] = tab;
var tabParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(40));
tabParams.bottomMargin = dp(6);
tabsLayout.addView(tabContainer, tabParams);
tabContainers[index] = tabContainer;
indicators[index] = indicator;
textViews[index] = tabText;
}
TextView footer = new TextView(getContext());
String version = FabricLoader.getInstance().getModContainer("parrotmod")
.map(m -> m.getMetadata().getVersion().getFriendlyString()).orElse("?.?.?");
footer.setText("ParrotMod v" + version);
footer.setTextSize(11);
footer.setTextColor(0x44FFFFFF);
footer.setText("v" + version);
footer.setTextSize(10);
footer.setTextColor(0x33FFFFFF);
footer.setGravity(Gravity.CENTER);
footer.setPadding(dp(16), dp(12), dp(16), dp(12));
sidebarContent.addView(footer);
root.setAlpha(0);
root.setScaleX(0.96f);
root.setScaleY(0.96f);
ObjectAnimator.ofFloat(root, View.ALPHA, 0, 1).setDuration(450).start();
ObjectAnimator.ofFloat(root, View.SCALE_X, 0.96f, 1).setDuration(450).start();
ObjectAnimator.ofFloat(root, View.SCALE_Y, 0.96f, 1).setDuration(450).start();
root.setScaleX(0.98f);
root.setScaleY(0.98f);
ObjectAnimator.ofFloat(root, View.ALPHA, 0, 1).setDuration(350).start();
ObjectAnimator.ofFloat(root, View.SCALE_X, 0.98f, 1).setDuration(350).start();
ObjectAnimator.ofFloat(root, View.SCALE_Y, 0.98f, 1).setDuration(350).start();
root.post(() -> {
tabButtons[0].callOnClick();
tabContainers[0].callOnClick();
});
return root;
@@ -139,4 +169,12 @@ public class SettingsFragment extends Fragment {
d.setColor(color);
return d;
}
private ShapeDrawable makeBorderedBg(int color, int strokeColor, float radius, float strokeWidth) {
ShapeDrawable d = new ShapeDrawable();
d.setCornerRadius(dp(radius));
d.setColor(color);
d.setStroke(dp(strokeWidth), strokeColor);
return d;
}
}