From 0212277cab22bb935050eee0c7254be9fd061d58 Mon Sep 17 00:00:00 2001 From: Patrick <147879351+WinniePatGG@users.noreply.github.com> Date: Sat, 20 Jun 2026 23:22:17 +0200 Subject: [PATCH] Rework Settings Menu --- .../parrotmod/ui/BaseTabFragment.java | 62 +++++++- .../parrotmod/ui/DiscordTabFragment.java | 50 +++--- .../parrotmod/ui/GeneralTabFragment.java | 23 +-- .../parrotmod/ui/InfoTabFragment.java | 26 ++-- .../parrotmod/ui/SettingsFragment.java | 142 +++++++++++------- 5 files changed, 201 insertions(+), 102 deletions(-) diff --git a/src/main/java/de/winniepat/parrotmod/ui/BaseTabFragment.java b/src/main/java/de/winniepat/parrotmod/ui/BaseTabFragment.java index 049dad6..c0760fd 100644 --- a/src/main/java/de/winniepat/parrotmod/ui/BaseTabFragment.java +++ b/src/main/java/de/winniepat/parrotmod/ui/BaseTabFragment.java @@ -2,6 +2,8 @@ package de.winniepat.parrotmod.ui; import icyllis.modernui.animation.ObjectAnimator; import icyllis.modernui.fragment.Fragment; +import icyllis.modernui.graphics.drawable.ShapeDrawable; +import icyllis.modernui.view.Gravity; import icyllis.modernui.view.View; import icyllis.modernui.view.ViewGroup; import icyllis.modernui.widget.LinearLayout; @@ -9,6 +11,9 @@ 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 int dp(float dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density); } @@ -25,12 +30,65 @@ public abstract class BaseTabFragment extends Fragment { return tv; } + protected void addSectionHeader(LinearLayout parent, String title) { + TextView tv = new TextView(getContext()); + tv.setText(title.toUpperCase()); + tv.setTextSize(11); + tv.setTextColor(COLOR_ACCENT); + // Letter spacing might not be available in all ModernUI versions, but let's try if it exists or just skip + var p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + p.topMargin = dp(24); + p.bottomMargin = dp(8); + p.leftMargin = dp(4); + parent.addView(tv, p); + } + + 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)); + + var cardParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + cardParams.bottomMargin = dp(8); + card.setLayoutParams(cardParams); + + LinearLayout textLayout = new LinearLayout(getContext()); + textLayout.setOrientation(LinearLayout.VERTICAL); + var textParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f); + card.addView(textLayout, textParams); + + TextView titleView = new TextView(getContext()); + titleView.setText(title); + titleView.setTextSize(15); + titleView.setTextColor(0xFFFFFFFF); + textLayout.addView(titleView); + + if (description != null) { + TextView descView = new TextView(getContext()); + descView.setText(description); + descView.setTextSize(12); + descView.setTextColor(0xFF888888); + textLayout.addView(descView); + } + + return card; + } + + protected ShapeDrawable makeRoundedBg(int color, float radius) { + ShapeDrawable d = new ShapeDrawable(); + d.setCornerRadius(dp(radius)); + d.setColor(color); + return d; + } + @Override public void onViewCreated(View view, icyllis.modernui.util.DataSet savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.setAlpha(0); - view.setTranslationY(dp(20)); + view.setTranslationY(dp(10)); ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(400).start(); - ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, dp(20), 0).setDuration(400).start(); + ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, dp(10), 0).setDuration(400).start(); } } diff --git a/src/main/java/de/winniepat/parrotmod/ui/DiscordTabFragment.java b/src/main/java/de/winniepat/parrotmod/ui/DiscordTabFragment.java index 32eac57..54db27b 100644 --- a/src/main/java/de/winniepat/parrotmod/ui/DiscordTabFragment.java +++ b/src/main/java/de/winniepat/parrotmod/ui/DiscordTabFragment.java @@ -1,5 +1,4 @@ package de.winniepat.parrotmod.ui; - import de.winniepat.parrotmod.config.ConfigManager; import icyllis.modernui.util.DataSet; import icyllis.modernui.view.LayoutInflater; @@ -7,60 +6,57 @@ import icyllis.modernui.view.View; import icyllis.modernui.view.ViewGroup; import icyllis.modernui.widget.LinearLayout; import icyllis.modernui.widget.Switch; -import icyllis.modernui.widget.TextView; -import icyllis.modernui.view.Gravity; +import icyllis.modernui.widget.ScrollView; public class DiscordTabFragment extends BaseTabFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, DataSet savedInstanceData) { + ScrollView scrollView = new ScrollView(getContext()); + scrollView.setFillViewport(true); + LinearLayout layout = new LinearLayout(getContext()); layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(dp(20), dp(20), dp(20), dp(20)); + layout.setPadding(dp(24), dp(8), dp(24), dp(24)); + scrollView.addView(layout); - addSwitch(layout, "Enable DiscordRPC", ConfigManager.getInstance().enableDiscordRPC, (v, checked) -> { + addSectionHeader(layout, "Discord Integration"); + + addSwitchRow(layout, "Enable DiscordRPC", "Show your game status on Discord", + ConfigManager.getInstance().enableDiscordRPC, (v, checked) -> { ConfigManager.getInstance().enableDiscordRPC = checked; ConfigManager.save(); }); - addSwitch(layout, "Show Biome in RPC", ConfigManager.getInstance().showBiomeInRPC, (v, checked) -> { + addSectionHeader(layout, "RPC Details"); + + addSwitchRow(layout, "Show Biome", "Display current biome in status", + ConfigManager.getInstance().showBiomeInRPC, (v, checked) -> { ConfigManager.getInstance().showBiomeInRPC = checked; ConfigManager.save(); }); - addSwitch(layout, "Show Held Item in RPC", ConfigManager.getInstance().showHeldItemInRPC, (v, checked) -> { + addSwitchRow(layout, "Show Held Item", "Display what you are holding", + ConfigManager.getInstance().showHeldItemInRPC, (v, checked) -> { ConfigManager.getInstance().showHeldItemInRPC = checked; ConfigManager.save(); }); - addSwitch(layout, "Show Health in RPC", ConfigManager.getInstance().showHealthInRPC, (v, checked) -> { + addSwitchRow(layout, "Show Health", "Display your current HP", + ConfigManager.getInstance().showHealthInRPC, (v, checked) -> { ConfigManager.getInstance().showHealthInRPC = checked; ConfigManager.save(); }); - return layout; + return scrollView; } - private void addSwitch(LinearLayout parent, String label, boolean checked, Switch.OnCheckedChangeListener listener) { - LinearLayout row = new LinearLayout(getContext()); - row.setOrientation(LinearLayout.HORIZONTAL); - row.setGravity(Gravity.CENTER_VERTICAL); - var rowParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - rowParams.bottomMargin = dp(12); - row.setLayoutParams(rowParams); - - TextView tv = new TextView(getContext()); - tv.setText(label); - tv.setTextSize(15); - tv.setTextColor(0xFFEEEEEE); - var labelParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f); - row.addView(tv, labelParams); - + private void addSwitchRow(LinearLayout parent, String title, String desc, boolean checked, Switch.OnCheckedChangeListener listener) { + LinearLayout card = createSettingCard(title, desc); Switch s = new Switch(getContext()); s.setChecked(checked); s.setOnCheckedChangeListener(listener); - row.addView(s); - - parent.addView(row); + card.addView(s); + parent.addView(card); } } diff --git a/src/main/java/de/winniepat/parrotmod/ui/GeneralTabFragment.java b/src/main/java/de/winniepat/parrotmod/ui/GeneralTabFragment.java index b523ff0..c21162a 100644 --- a/src/main/java/de/winniepat/parrotmod/ui/GeneralTabFragment.java +++ b/src/main/java/de/winniepat/parrotmod/ui/GeneralTabFragment.java @@ -15,23 +15,26 @@ 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(20), dp(20), dp(20), dp(20)); + layout.setPadding(dp(24), dp(8), dp(24), dp(24)); - layout.addView(makeLabel("Minecraft Version: " + Minecraft.getInstance().getLaunchedVersion())); + addSectionHeader(layout, "Client Info"); + layout.addView(createSettingCard("Minecraft Version", Minecraft.getInstance().getLaunchedVersion())); + addSectionHeader(layout, "Config Management"); + + LinearLayout reloadCard = createSettingCard("Reload Config", "Reset all settings to saved values"); Button reloadBtn = new Button(getContext()); - reloadBtn.setText("Reload Config"); + reloadBtn.setText("Reload"); reloadBtn.setOnClickListener(v -> ConfigManager.load()); - var p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - p.topMargin = dp(24); - layout.addView(reloadBtn, p); + reloadCard.addView(reloadBtn); + layout.addView(reloadCard); + LinearLayout saveCard = createSettingCard("Save Config", "Persist current settings to disk"); Button saveBtn = new Button(getContext()); - saveBtn.setText("Save Config"); + saveBtn.setText("Save"); saveBtn.setOnClickListener(v -> ConfigManager.save()); - var p2 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - p2.topMargin = dp(12); - layout.addView(saveBtn, p2); + saveCard.addView(saveBtn); + layout.addView(saveCard); return layout; } diff --git a/src/main/java/de/winniepat/parrotmod/ui/InfoTabFragment.java b/src/main/java/de/winniepat/parrotmod/ui/InfoTabFragment.java index 4d37f67..1120f8a 100644 --- a/src/main/java/de/winniepat/parrotmod/ui/InfoTabFragment.java +++ b/src/main/java/de/winniepat/parrotmod/ui/InfoTabFragment.java @@ -5,6 +5,7 @@ import icyllis.modernui.view.LayoutInflater; import icyllis.modernui.view.View; import icyllis.modernui.view.ViewGroup; import icyllis.modernui.widget.LinearLayout; +import icyllis.modernui.widget.ScrollView; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.Minecraft; @@ -12,31 +13,34 @@ public class InfoTabFragment extends BaseTabFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, DataSet savedInstanceData) { + ScrollView scrollView = new ScrollView(getContext()); + scrollView.setFillViewport(true); + LinearLayout layout = new LinearLayout(getContext()); layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(dp(20), dp(20), dp(20), dp(20)); + layout.setPadding(dp(24), dp(8), dp(24), dp(24)); + scrollView.addView(layout); + addSectionHeader(layout, "Mod Versions"); String parrotVersion = FabricLoader.getInstance().getModContainer("parrotmod").orElseThrow().getMetadata().getVersion().getFriendlyString(); - layout.addView(makeLabel("Parrotmod version: " + parrotVersion)); + layout.addView(createSettingCard("Parrotmod", parrotVersion)); String mcVersion = FabricLoader.getInstance().getModContainer("minecraft").orElseThrow().getMetadata().getVersion().getFriendlyString(); - layout.addView(makeLabel("Minecraft Version: " + Minecraft.getInstance().getLaunchedVersion() + " (" + mcVersion + ")")); + layout.addView(createSettingCard("Minecraft", Minecraft.getInstance().getLaunchedVersion() + " (" + mcVersion + ")")); String loaderVersion = FabricLoader.getInstance().getModContainer("fabricloader").orElseThrow().getMetadata().getVersion().getFriendlyString(); - layout.addView(makeLabel("Fabric Loader Version: " + loaderVersion)); + layout.addView(createSettingCard("Fabric Loader", loaderVersion)); - int modCount = FabricLoader.getInstance().getAllMods().size(); - layout.addView(makeLabel("Loaded Mods: " + modCount)); - - layout.addView(makeLabel("Java Version: " + System.getProperty("java.version") + " (" + System.getProperty("os.arch") + ")")); - layout.addView(makeLabel("OS: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ")")); + 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") + ")")); long maxMemory = Runtime.getRuntime().maxMemory(); long totalMemory = Runtime.getRuntime().totalMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); long usedMemory = totalMemory - freeMemory; - layout.addView(makeLabel(String.format("Memory: %dMB / %dMB", usedMemory / 1024 / 1024, maxMemory / 1024 / 1024))); + layout.addView(createSettingCard("Memory Usage", String.format("%dMB / %dMB", usedMemory / 1024 / 1024, maxMemory / 1024 / 1024))); - return layout; + return scrollView; } } diff --git a/src/main/java/de/winniepat/parrotmod/ui/SettingsFragment.java b/src/main/java/de/winniepat/parrotmod/ui/SettingsFragment.java index 915bbb0..36ec526 100644 --- a/src/main/java/de/winniepat/parrotmod/ui/SettingsFragment.java +++ b/src/main/java/de/winniepat/parrotmod/ui/SettingsFragment.java @@ -8,47 +8,71 @@ import icyllis.modernui.view.*; import icyllis.modernui.widget.*; import icyllis.modernui.graphics.drawable.ShapeDrawable; import icyllis.modernui.graphics.*; +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; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, DataSet savedInstanceState) { - LinearLayout root = new LinearLayout(getContext()); - root.setOrientation(LinearLayout.HORIZONTAL); - root.setLayoutParams(new FrameLayout.LayoutParams( - dp(700), dp(450), Gravity.CENTER)); + // Main container with shadow/margin if needed, but here it's centering + FrameLayout root = new FrameLayout(getContext()); + root.setLayoutParams(new FrameLayout.LayoutParams(dp(720), dp(480), Gravity.CENTER)); - root.setAlpha(0); - root.setScaleX(0.95f); - root.setScaleY(0.95f); - ObjectAnimator.ofFloat(root, View.ALPHA, 0, 1).setDuration(400).start(); - ObjectAnimator.ofFloat(root, View.SCALE_X, 0.95f, 1).setDuration(400).start(); - ObjectAnimator.ofFloat(root, View.SCALE_Y, 0.95f, 1).setDuration(400).start(); + // Background + View bg = new View(getContext()); + bg.setBackground(makeRoundedBg(COLOR_BACKGROUND, 16)); + root.addView(bg); - LinearLayout sidebar = new LinearLayout(getContext()); - sidebar.setOrientation(LinearLayout.VERTICAL); - sidebar.setPadding(dp(12), dp(16), dp(12), dp(16)); - sidebar.setBackground(makeRoundedBg(0xEE161625)); + LinearLayout layout = new LinearLayout(getContext()); + layout.setOrientation(LinearLayout.HORIZONTAL); + root.addView(layout); + + // Sidebar Container + FrameLayout sidebarContainer = new FrameLayout(getContext()); + var sidebarParams = new LinearLayout.LayoutParams(dp(170), ViewGroup.LayoutParams.MATCH_PARENT); + layout.addView(sidebarContainer, sidebarParams); + + View sidebarBg = new View(getContext()); + sidebarBg.setBackground(makeRoundedBg(COLOR_SIDEBAR, 16)); + // We only want right side to be sharp, but for simplicity we just overlap or use a large radius + sidebarContainer.addView(sidebarBg); + + LinearLayout sidebarContent = new LinearLayout(getContext()); + sidebarContent.setOrientation(LinearLayout.VERTICAL); + sidebarContent.setPadding(0, dp(24), 0, dp(16)); // Removed horizontal padding to allow edge indicator + sidebarContainer.addView(sidebarContent, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); TextView title = new TextView(getContext()); - title.setText("ParrotMod"); - title.setTextSize(20); + title.setText("PARROT"); + title.setTextSize(22); title.setGravity(Gravity.CENTER); - title.setTextColor(0xFFFFFFFF); - title.setPadding(0, 0, 0, dp(20)); - sidebar.addView(title); + title.setTextColor(COLOR_ACCENT); + title.setPadding(dp(16), 0, dp(16), dp(32)); // Added horizontal padding back to title + sidebarContent.addView(title); - var sidebarParams = new LinearLayout.LayoutParams(dp(160), ViewGroup.LayoutParams.MATCH_PARENT); - root.addView(sidebar, sidebarParams); + // Tabs container + FrameLayout tabsContainer = new FrameLayout(getContext()); + sidebarContent.addView(tabsContainer, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); + LinearLayout tabsLayout = new LinearLayout(getContext()); + tabsLayout.setOrientation(LinearLayout.VERTICAL); + // Center tabs vertically in available space + tabsContainer.addView(tabsLayout, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); + + + // Content Area FrameLayout contentArea = new FrameLayout(getContext()); contentArea.setId(R.id.content); - contentArea.setBackground(makeRoundedBg(0xEE101018)); - - var contentParams = new LinearLayout.LayoutParams( - 0, ViewGroup.LayoutParams.MATCH_PARENT, 1f); - contentParams.leftMargin = dp(6); - root.addView(contentArea, contentParams); + var contentParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f); + layout.addView(contentArea, contentParams); String[] tabNames = {"Info", "General", "Discord"}; Fragment[] tabFragments = { @@ -57,46 +81,60 @@ public class SettingsFragment extends Fragment { new DiscordTabFragment() }; - getChildFragmentManager().beginTransaction() - .replace(contentArea.getId(), tabFragments[0]) - .commitNow(); - - Button[] tabs = new Button[tabNames.length]; + Button[] tabButtons = new Button[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(15); + tab.setTextSize(14); tab.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); - tab.setPadding(dp(16), dp(8), dp(16), dp(8)); + tab.setPadding(dp(32), 0, dp(16), 0); tab.setBackground(null); + tab.setTextColor(COLOR_TEXT_DIM); tab.setOnClickListener(v -> { getChildFragmentManager().beginTransaction() .setReorderingAllowed(true) .replace(contentArea.getId(), tabFragments[index]) .commit(); - for (Button b : tabs) { - b.setTextColor(0xFFAAAAAA); - b.setBackground(null); + + for (int j = 0; j < tabButtons.length; j++) { + boolean selected = (j == index); + tabButtons[j].setTextColor(selected ? 0xFFFFFFFF : COLOR_TEXT_DIM); + tabButtons[j].setBackground(selected ? makeRoundedBg(0x12FFFFFF, 8) : null); } - tab.setTextColor(0xFFFFFFFF); - tab.setBackground(makeRoundedBg(0x33FFFFFF)); + }); - var tabParams = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, dp(40)); - tabParams.bottomMargin = dp(8); - sidebar.addView(tab, tabParams); - tabs[index] = tab; + var tabParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(44)); + tabParams.bottomMargin = dp(4); + tabsLayout.addView(tab, tabParams); + tabButtons[index] = tab; } - tabs[0].setTextColor(0xFFFFFFFF); - tabs[0].setBackground(makeRoundedBg(0x33FFFFFF)); - for (int i = 1; i < tabs.length; i++) { - tabs[i].setTextColor(0xFFAAAAAA); - tabs[i].setBackground(null); - } + // Footer + 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.setGravity(Gravity.CENTER); + footer.setPadding(dp(16), dp(12), dp(16), dp(12)); + sidebarContent.addView(footer); + + // Entrance Animation + 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(); + + // Initial tab + root.post(() -> { + tabButtons[0].callOnClick(); + }); return root; } @@ -105,9 +143,9 @@ public class SettingsFragment extends Fragment { return (int) (dp * getContext().getResources().getDisplayMetrics().density); } - private ShapeDrawable makeRoundedBg(int color) { + private ShapeDrawable makeRoundedBg(int color, float radius) { ShapeDrawable d = new ShapeDrawable(); - d.setCornerRadius(dp(12)); + d.setCornerRadius(dp(radius)); d.setColor(color); return d; }