Files
common/src/main/java/org/saturnclient/ui/elements/ChipGroup.java
T
selimaj-devandclaude dfebd2367d 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]>
2026-09-27 16:59:51 +02:00

86 lines
2.8 KiB
Java

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