Compare commits

..
9 Commits
Author SHA1 Message Date
selimaj-dev b14c5c4ffa Merge pull request 'Make the default theme near-neutral and tone down the presets' (#15) from neutral-themes into master
Reviewed-on: #15
2026-10-03 04:12:16 +00:00
selimaj-devandclaude fd9ec3222c Wrap the theme presets onto more rows when the cards get too narrow
With five presets, the cards at a larger menu scale were narrower than
the widgets in their previews.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 06:11:48 +02:00
selimaj-devandclaude 917eba1e17 Tone down the Midnight and Amethyst themes
Their tinted surfaces turned muddy with the warm world showing through,
and their bright pastel accents clashed with Minecraft's colors. The
surfaces are now close to neutral and the accents less saturated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 07:49:35 +02:00
selimaj-devandclaude 2ef28c9e14 Replace the Emerald theme with a black-and-white Mono one
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 07:49:28 +02:00
selimaj-devandclaude e395068e76 Make the default theme a near-neutral one with a frost accent
The gold over a warm Minecraft world was too much. The new default keeps
the surfaces close to neutral grey so the world showing through the glass
doesn't tint them, uses a pale frost for the accent, and keeps Saturn's
butterscotch only on the scrollbar. The old colors stay as "Saturn Gold".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 07:49:16 +02:00
selimaj-dev e8f686b2d3 Merge pull request 'Remove the upgrade of opaque theme colors from before 0.1.3' (#14) from drop-color-upgrade into master
Reviewed-on: #14
2026-09-30 19:49:03 +00:00
selimaj-devandclaude df5afe99e3 Remove the upgrade of opaque theme colors from before 0.1.3
Configs saved before 0.1.3 (without a menu blur) had their preset
background and primary colors made translucent on load. It's no longer
needed, so Theme drops it, along with ConfigManager.isSaved, which only
it used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:47:45 +02:00
selimaj-dev 653738568a Merge pull request 'Rename the Ref methods that share a name with vanilla methods' (#13) from explicit-refs into master
Reviewed-on: #13
2026-09-30 16:03:45 +00:00
selimaj-devandclaude a6a324a61a Rename the Ref methods that share a name with vanilla methods
WindowRef.getWidth/getHeight and ItemStackRef.isEmpty/getMaxDamage
match methods of the classes they're mixed into, so the mixins can't
implement them explicitly. With Mojang's names, Window.getWidth is
also the framebuffer's width rather than the window's. They're now
getWindowWidth/getWindowHeight and isEmptyStack/getMaxItemDamage
(with getItemDamage to match).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 17:42:48 +02:00
8 changed files with 50 additions and 62 deletions
@@ -1,7 +1,11 @@
package org.saturnclient.common.ref.game;
/**
* An item stack. Its methods are implemented in ItemStackMixin, so their names must not match any of
* ItemStack's own methods.
*/
public interface ItemStackRef {
public boolean isEmpty();
public int getMaxDamage();
public int getDamage();
public boolean isEmptyStack();
public int getMaxItemDamage();
public int getItemDamage();
}
@@ -1,9 +1,15 @@
package org.saturnclient.common.ref.render;
/**
* The game window. Its methods are implemented in WindowMixin, so their names must not match any of
* Window's own methods.
*/
public interface WindowRef {
public int getWidth();
/** The window's width in screen coordinates */
public int getWindowWidth();
public int getHeight();
/** The window's height in screen coordinates */
public int getWindowHeight();
public int getFramebufferWidth();
@@ -163,14 +163,6 @@ public class ConfigManager {
}
}
/**
* Whether the config file has a value for {@code name} in this namespace,
* which tells which version last saved it.
*/
public boolean isSaved(String name) {
return getSaved(name) != null;
}
private JsonNode getSaved(String name) {
JsonNode source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null || !source.isObject())
@@ -10,26 +10,27 @@ import org.saturnclient.config.property.SelectProperty;
public class Theme {
public static ConfigManager config;
// Main UI background (slightly warm dark to match Saturn tone), translucent over the world
public static final ColorProperty BACKGROUND = Property.color(0xD112100D);
// Main UI background (a near-neutral dark, so the warm world showing through
// doesn't muddy it), translucent over the world
public static final ColorProperty BACKGROUND = Property.color(0xD1131417);
// Default text color
public static final ColorProperty FOREGROUND = Property.color(0xFFF2E6D2);
public static final ColorProperty FOREGROUND = Property.color(0xFFECEFF3);
// Widget default background (normal state), translucent over the world
public static final ColorProperty PRIMARY = Property.color(0xB81C1915);
public static final ColorProperty PRIMARY = Property.color(0xB81E2025);
// Widget text color
public static final ColorProperty PRIMARY_FG = Property.color(0xFFEAD9B8);
public static final ColorProperty PRIMARY_FG = Property.color(0xFFD5DAE1);
// Saturn brand color (hovered / enabled / selected)
public static final ColorProperty ACCENT = Property.color(0xFFE2BF7D);
// Saturn brand color, a pale frost (hovered / enabled / selected)
public static final ColorProperty ACCENT = Property.color(0xFFD3DCE6);
// Text/icons shown on accent background
public static final ColorProperty ACCENT_FG = Property.color(0xFF1A140A);
public static final ColorProperty ACCENT_FG = Property.color(0xFF15181C);
// Misc colors
public static final ColorProperty SCROLL = Property.color(0xFFE2BF7D);
// Misc colors: the one warm touch, Saturn's butterscotch
public static final ColorProperty SCROLL = Property.color(0xFFD9B98A);
// UI metrics
public static final IntProperty BG_RADIUS = Property.integer(12).range(0, 24);
@@ -62,10 +63,11 @@ public class Theme {
}
public static final List<Preset> PRESETS = List.of(
new Preset("Saturn", 0xD112100D, 0xFFF2E6D2, 0xB81C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D),
new Preset("Midnight", 0xD10E1117, 0xFFE4EAF5, 0xB8171C26, 0xFFC9D4E8, 0xFF8FB3FF, 0xFF0A1224, 0xFF8FB3FF),
new Preset("Amethyst", 0xD1120F17, 0xFFEDE4F7, 0xB81C1724, 0xFFD8C9EB, 0xFFC79BFF, 0xFF1A0F2A, 0xFFC79BFF),
new Preset("Emerald", 0xD10D1210, 0xFFE2F2EA, 0xB816201B, 0xFFC4E0D2, 0xFF7FD1A8, 0xFF0A1F15, 0xFF7FD1A8));
new Preset("Saturn", 0xD1131417, 0xFFECEFF3, 0xB81E2025, 0xFFD5DAE1, 0xFFD3DCE6, 0xFF15181C, 0xFFD9B98A),
new Preset("Saturn Gold", 0xD112100D, 0xFFF2E6D2, 0xB81C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D),
new Preset("Mono", 0xD1121212, 0xFFF0F0F0, 0xB81C1C1C, 0xFFDADADA, 0xFFF0F0F0, 0xFF121212, 0xFFF0F0F0),
new Preset("Midnight", 0xD1121418, 0xFFE5E9F0, 0xB81C2027, 0xFFD8DEE9, 0xFF8FAFCF, 0xFF10151D, 0xFF8FAFCF),
new Preset("Amethyst", 0xD1141317, 0xFFECE8F1, 0xB81F1D24, 0xFFDAD3E3, 0xFFB49CCB, 0xFF19131F, 0xFFB49CCB));
public static void init(ConfigManager parent) {
config = new ConfigManager(parent, "UI Style");
@@ -81,29 +83,6 @@ public class Theme {
config.property("Widget Radius", WIDGET_RADIUS, "Corner rounding of buttons and cards");
config.property("Font", FONT, "Font of every Saturn screen");
if (!parent.isSaved(Config.MENU_BLUR)) {
upgradeOpaqueColors();
}
}
/**
* Before 0.1.3 panels and cards were made translucent in code, and the
* background and primary colors were saved fully opaque. Saved colors that
* are an old default or preset get the translucency the colors now carry
* themselves; colors the player picked are left as they are. Runs only on
* configs from older versions, so a preset color later set opaque on
* purpose stays opaque.
*/
private static void upgradeOpaqueColors() {
for (Preset preset : PRESETS) {
if (BACKGROUND.value == (preset.background() | 0xFF000000)) {
BACKGROUND.value = preset.background();
}
if (PRIMARY.value == (preset.primary() | 0xFF000000)) {
PRIMARY.value = preset.primary();
}
}
}
/**
@@ -75,16 +75,16 @@ public class ArmorDisplayMod extends Mod implements HudMod {
int row = 0;
if (useMainHand.value && !mainHand.isEmpty()) {
if (useMainHand.value && !mainHand.isEmptyStack()) {
row = renderSlot(scope, mainHand, row);
}
if (!helmet.isEmpty())
if (!helmet.isEmptyStack())
row = renderSlot(scope, helmet, row);
if (!chestplate.isEmpty())
if (!chestplate.isEmptyStack())
row = renderSlot(scope, chestplate, row);
if (!leggings.isEmpty())
if (!leggings.isEmptyStack())
row = renderSlot(scope, leggings, row);
if (!boots.isEmpty())
if (!boots.isEmptyStack())
row = renderSlot(scope, boots, row);
}
@@ -94,8 +94,8 @@ public class ArmorDisplayMod extends Mod implements HudMod {
private int renderSlot(RenderScope scope, ItemStackRef item, int row) {
scope.drawItem(item, 0, 15 * row);
if (item.getMaxDamage() > 0) {
int remaining = item.getMaxDamage() - item.getDamage();
if (item.getMaxItemDamage() > 0) {
int remaining = item.getMaxItemDamage() - item.getItemDamage();
scope.drawText(0.5f, String.valueOf(remaining),
17, (15 * row) + 3,
layout.font.value, layout.fgColor.value);
@@ -12,6 +12,8 @@ import org.saturnclient.ui.resources.Fonts;
*/
public class ThemePresetCard extends Element {
public static final int HEIGHT = 92;
/** The narrowest card that still fits its preview's widgets */
public static final int MIN_WIDTH = 100;
private static final int PADDING = 10;
private static final int PREVIEW_HEIGHT = 44;
@@ -20,9 +20,9 @@ public class ImageTexture extends Element {
if (sprite.toString().endsWith(".svg")) {
MinecraftClientRef client = Providers.saturn.getClient();
int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWidth());
int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWindowWidth());
int renderHeight = (int) (height * client.getWindow().getFramebufferHeight()
/ client.getWindow().getHeight());
/ client.getWindow().getWindowHeight());
IdentifierRef pngId = SvgTexture.getSvg(sprite, renderWidth * 2, renderHeight * 2);
@@ -102,15 +102,20 @@ public class ClientSettings extends PanelScreen {
Map<String, Property> properties = Theme.config.getProperties();
int y = PropertyList.drawSection(scroll, "Presets", 0) + 10;
int cardWidth = (width - PRESET_GAP * (Theme.PRESETS.size() - 1)) / Theme.PRESETS.size();
// One row when the cards fit, otherwise as many rows as they need
int columns = Math.max(1, Math.min(Theme.PRESETS.size(),
(width + PRESET_GAP) / (ThemePresetCard.MIN_WIDTH + PRESET_GAP)));
int rows = (Theme.PRESETS.size() + columns - 1) / columns;
int cardWidth = (width - PRESET_GAP * (columns - 1)) / columns;
for (int i = 0; i < Theme.PRESETS.size(); i++) {
// Rebuild so the color inputs show the preset's colors
scroll.draw(new ThemePresetCard(Theme.PRESETS.get(i), cardWidth, this::init)
.position(i * (cardWidth + PRESET_GAP), y));
.position(i % columns * (cardWidth + PRESET_GAP),
y + i / columns * (ThemePresetCard.HEIGHT + PRESET_GAP)));
}
y += ThemePresetCard.HEIGHT + SECTION_GAP;
y += rows * ThemePresetCard.HEIGHT + (rows - 1) * PRESET_GAP + SECTION_GAP;
y = PropertyList.drawSection(scroll, "Colors", y) + 10;
y = ColorGrid.draw(scroll, properties, width, y) + SECTION_GAP;