Redesign the mod menu
The mod menu is now a translucent panel beside a navigation rail, with a header showing how many mods are enabled, a search box, category filters built from the mods' tags and a three-column grid of mod cards. Each card shows the mod's icon, name and description with a settings button and an on/off switch. The layout shrinks to fit smaller windows. New reusable pieces: Panel, Label, ChipGroup, SearchInput, Toggle.drawSwitch, Theme helpers for panel, border, muted text and body font, and Fonts.ellipsize. The sidebar and card icons are Lucide SVGs, and seven mod icons were redrawn in the same style. Scroll can skip its background and be cleared, and no longer passes clicks outside its bounds to children scrolled out of view. Input checks overflow and places the cursor at the scale its text is drawn at. Screens that are not redesigned yet move the wider sidebar so it keeps its gap. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package org.saturnclient.ui.elements;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
/**
|
||||
* A row of pill buttons where exactly one is selected, such as category filters.
|
||||
*/
|
||||
public class ChipGroup extends Element {
|
||||
private static final int HEIGHT = 28;
|
||||
private static final int PADDING = 14;
|
||||
private static final int GAP = 8;
|
||||
private static final float TEXT_SCALE = 0.62f;
|
||||
|
||||
private final List<String> options;
|
||||
private final Consumer<String> onSelect;
|
||||
private String selected;
|
||||
|
||||
public ChipGroup(List<String> options, String selected, Consumer<String> onSelect) {
|
||||
this.options = options;
|
||||
this.selected = selected;
|
||||
this.onSelect = onSelect;
|
||||
this.height = HEIGHT;
|
||||
this.width = chipX(options.size()) - GAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
int font = Theme.FONT.value;
|
||||
int textY = (HEIGHT - (int) (Fonts.getHeight() * TEXT_SCALE)) / 2;
|
||||
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
String option = options.get(i);
|
||||
int x = chipX(i);
|
||||
int w = chipWidth(option);
|
||||
boolean active = option.equals(selected);
|
||||
boolean hovered = ctx.isHovering(x, 0, w, HEIGHT);
|
||||
|
||||
if (active) {
|
||||
renderScope.drawRoundedRectangle(x, 0, w, HEIGHT, HEIGHT / 2, Theme.ACCENT.value);
|
||||
} else {
|
||||
renderScope.drawRoundedRectangle(x, 0, w, HEIGHT, HEIGHT / 2, 1,
|
||||
Theme.withAlpha(0.05f, Theme.FOREGROUND.value),
|
||||
hovered ? Theme.withAlpha(0.25f, Theme.FOREGROUND.value) : Theme.getBorder());
|
||||
}
|
||||
|
||||
renderScope.drawText(TEXT_SCALE, option, x + PADDING, textY, font,
|
||||
active ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
String option = options.get(i);
|
||||
int x = chipX(i);
|
||||
|
||||
if (mouseX >= x && mouseX <= x + chipWidth(option)) {
|
||||
if (!option.equals(selected)) {
|
||||
selected = option;
|
||||
onSelect.accept(option);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int chipX(int index) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < index; i++) {
|
||||
x += chipWidth(options.get(i)) + GAP;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private static int chipWidth(String option) {
|
||||
return (int) (Fonts.getWidth(option, Theme.FONT.value) * TEXT_SCALE) + PADDING * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.saturnclient.ui.elements;
|
||||
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
/**
|
||||
* A line of text whose content, font and color are read on every frame, so it
|
||||
* follows state and theme changes without rebuilding the screen.
|
||||
*/
|
||||
public class Label extends Element {
|
||||
private final Supplier<String> text;
|
||||
private final float textScale;
|
||||
private final IntSupplier font;
|
||||
private final IntSupplier color;
|
||||
|
||||
public Label(Supplier<String> text, float textScale, IntSupplier font, IntSupplier color) {
|
||||
this.text = text;
|
||||
this.textScale = textScale;
|
||||
this.font = font;
|
||||
this.color = color;
|
||||
this.height = (int) (Fonts.getHeight() * textScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawText(textScale, text.get(), 0, 0, font.getAsInt(), color.getAsInt());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.saturnclient.ui.elements;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
/**
|
||||
* The translucent, outlined surface that screens lay their content on.
|
||||
*/
|
||||
public class Panel extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
drawBackground(renderScope, width, height);
|
||||
}
|
||||
|
||||
public static void drawBackground(RenderScope renderScope, int width, int height) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, 1,
|
||||
Theme.getPanelBg(), Theme.getBorder());
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class Scroll extends Element {
|
||||
private static IntProperty scrollBarPadding = Property.integer(5);
|
||||
|
||||
int padding = 0;
|
||||
boolean background = true;
|
||||
|
||||
protected List<Element> children = new ArrayList<>();
|
||||
int scroll = 0;
|
||||
@@ -30,12 +31,31 @@ public class Scroll extends Element {
|
||||
ElementRenderer.draw(children, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the scroll's own background, for when it sits on a {@link Panel}.
|
||||
*/
|
||||
public Scroll noBackground() {
|
||||
this.background = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes every child and scrolls back to the top.
|
||||
*/
|
||||
public void clear() {
|
||||
children.clear();
|
||||
scroll = 0;
|
||||
maxScroll = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
calculateMaxScroll();
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height,
|
||||
Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
|
||||
if (background) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height,
|
||||
Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
|
||||
}
|
||||
|
||||
renderScope.enableScissor(padding, padding, width - padding, height - padding);
|
||||
renderScope.getMatrixStack().push();
|
||||
@@ -117,6 +137,15 @@ public class Scroll extends Element {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
// Children scrolled out of view still have positions under the rest of the
|
||||
// screen, so only clicks inside the scroll's own bounds may reach them.
|
||||
if (mouseX < 0 || mouseY < 0 || mouseX > width || mouseY > height) {
|
||||
for (Element child : children) {
|
||||
child.focused = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user