Compare commits

...
1 Commits
Author SHA1 Message Date
selimaj-devandclaude 4d9c629eaf Frost translucent panels with glass
Panels whose background isn't solid draw the blurred game behind them,
cut to the same rounded rectangle texture as the background, where the
version supports glass. Panel screens then leave the rest of the game
unblurred.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 20:43:44 +02:00
5 changed files with 65 additions and 9 deletions
@@ -28,6 +28,12 @@ public interface SaturnProvider {
*/
public IdentifierRef getPlayerSkin();
/**
* Whether this version draws glass (the blurred game behind translucent
* panels), so screens needn't blur the whole background.
*/
public boolean supportsGlass();
/** Shows a warning toast. Safe to call from any thread. */
public void showWarning(String title, String message);
}
@@ -1,5 +1,6 @@
package org.saturnclient.ui;
import org.saturnclient.common.provider.Providers;
import org.saturnclient.ui.components.Sidebar;
import org.saturnclient.ui.elements.ImageTexture;
import org.saturnclient.ui.elements.Panel;
@@ -38,6 +39,9 @@ public abstract class PanelScreen extends SaturnScreen {
@Override
public final void ui() {
// The panels are frosted themselves, so the rest of the game stays clear.
blurBackground = !Providers.saturn.supportsGlass();
panelWidth = Math.min(MAX_WIDTH, width - MARGIN * 2 - Sidebar.WIDTH - GAP);
panelHeight = Math.min(MAX_HEIGHT, height - MARGIN * 2);
@@ -34,6 +34,14 @@ public interface RenderScope {
if (color == 0)
return;
drawTexture(getRoundedRectangle(width, height, radius), x, y, 0, 0, width, height, color);
}
/**
* The white rounded rectangle that {@link #drawRoundedRectangle} tints, as a
* cached texture.
*/
default IdentifierRef getRoundedRectangle(int width, int height, int radius) {
radius = Math.min(radius, width / 2);
radius = Math.min(radius, height / 2);
@@ -42,7 +50,24 @@ public interface RenderScope {
width, height,
radius, radius);
drawSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, x, y, width, height, color);
return getSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, width, height);
}
/**
* Frosts a rounded rectangle for a translucent background to be drawn over:
* the game behind the screen, blurred, cut to exactly the shape
* {@link #drawRoundedRectangle} draws. Draws nothing on versions without
* glass.
*/
default void drawGlass(int x, int y, int width, int height, int radius) {
drawGlass(getRoundedRectangle(width, height, radius), x, y, width, height);
}
/**
* Draws the blurred game behind the screen where {@code mask}, stretched
* over the area, is opaque.
*/
default void drawGlass(IdentifierRef mask, int x, int y, int width, int height) {
}
/**
@@ -89,15 +114,22 @@ public interface RenderScope {
* {@code color}.
*/
private void drawSvgShape(String shape, String cacheKey, int x, int y, int width, int height, int color) {
drawTexture(getSvgShape(shape, cacheKey, width, height), x, y, 0, 0, width, height, color);
}
/**
* {@code shape} (SVG elements in a {@code width} x {@code height} canvas,
* drawn in white) rendered to a cached texture.
*/
private IdentifierRef getSvgShape(String shape, String cacheKey, int width, int height) {
String svg = String.format(
"<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>%s</svg>",
width, height, shape);
InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
drawTexture(SvgTexture.getSvg(svgStream,
IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
width * 4, height * 4), x, y, 0, 0, width, height, color);
return SvgTexture.getSvg(svgStream, IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
width * 4, height * 4);
}
void drawBorder(int x, int y, int width, int height, int color);
@@ -46,11 +46,11 @@ public abstract class SaturnScreen {
}
/**
* How much to blur the game behind this screen right now: the client's
* setting, faded in over the screen's first moments.
* How much to blur the game behind this screen and its glass right now: the
* client's setting, faded in over the screen's first moments.
*/
public int getBackgroundBlur() {
if (!blurBackground || start == null) {
public int getBlurRadius() {
if (start == null) {
return 0;
}
@@ -58,6 +58,14 @@ public abstract class SaturnScreen {
return Math.round(Config.backgroundBlur.value * Math.min(elapsed / (float) BLUR_FADE_IN, 1f));
}
/**
* How much to blur the whole game behind this screen right now, or 0 when it
* isn't.
*/
public int getBackgroundBlur() {
return blurBackground ? getBlurRadius() : 0;
}
/**
* The GUI scale setting this screen follows; the menu scale unless the
* screen is another kind.
@@ -6,7 +6,8 @@ import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
/**
* The translucent, outlined surface that screens lay their content on.
* The translucent, outlined surface that screens lay their content on,
* frosted over the game where glass is supported.
*/
public class Panel extends Element {
@@ -16,6 +17,11 @@ public class Panel extends Element {
}
public static void drawBackground(RenderScope renderScope, int width, int height) {
// Frosted behind a background that isn't solid
if (Theme.getPanelBg() >>> 24 < 0xFF) {
renderScope.drawGlass(0, 0, width, height, Theme.BG_RADIUS.value);
}
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, 1,
Theme.getPanelBg(), Theme.getBorder());
}