Skip to content

Commit

Permalink
Application: Show splash screen at startup while loading is in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadelessFox committed May 16, 2024
1 parent f3975ba commit d2390b7
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
public class Application implements com.shade.platform.model.app.Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);

private final Preferences preferences;
private final ServiceManager serviceManager;
private Preferences preferences;
private ServiceManager serviceManager;

private MessageBusConnection connection;
private JFrame frame;
Expand All @@ -79,11 +79,6 @@ public class Application implements com.shade.platform.model.app.Application {
configureLogger();
}

public Application() {
this.preferences = Preferences.userRoot().node("decima-explorer");
this.serviceManager = new ServiceManager(getConfigPath());
}

@NotNull
public static Application getInstance() {
return (Application) ApplicationManager.getApplication();
Expand All @@ -102,12 +97,25 @@ public void start(@NotNull String[] args) {
log.info("CLI Arguments: {}", Arrays.asList(args));
log.info("-------------------");

if (args.length == 0) {
Splash.getInstance().show();
}

Splash.getInstance().set("Loading services");

this.preferences = Preferences.userRoot().node("decima-explorer");
this.serviceManager = new ServiceManager(getConfigPath());

if (args.length > 0) {
ApplicationCLI.execute(args);
}

Splash.getInstance().show();

connection = MessageBus.getInstance().connect();

Splash.getInstance().set("Configuring UI");

configureUI();
frame = new JFrame();
configureFrame(frame);
Expand Down Expand Up @@ -151,12 +159,16 @@ public void selectionCleared() {
panel.add(ViewManager.getInstance().getComponent(), BorderLayout.CENTER);
panel.add(statusBar, BorderLayout.SOUTH);

Splash.getInstance().set("Done");

frame.setTitle(getApplicationTitle());
frame.setIconImages(FlatSVGUtils.createWindowIconImages("/icons/application.svg"));
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setContentPane(panel);
frame.setVisible(true);

Splash.getInstance().hide();

Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
try {
serviceManager.persist();
Expand Down
182 changes: 182 additions & 0 deletions modules/decima-ui/src/main/java/com/shade/decima/ui/Splash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package com.shade.decima.ui;

import com.formdev.flatlaf.util.SystemInfo;
import com.shade.decima.BuildConfig;
import com.shade.util.NotNull;
import com.shade.util.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.Objects;
import java.util.Random;

public class Splash {
private static final Splash INSTANCE = new Splash();
private SplashFrame frame;

private Splash() {
// Prevents instantiation
}

@NotNull
public static Splash getInstance() {
return INSTANCE;
}

public void show() {
if (frame == null) {
frame = new SplashFrame();
frame.setVisible(true);
}
}

public void hide() {
if (frame != null) {
frame.dispose();
frame = null;
}
}

public void set(@Nullable String status) {
if (frame != null) {
frame.component.setStatus(status);
}
}

private static class SplashFrame extends JFrame {
private final SplashComponent component;

public SplashFrame() {
add(component = new SplashComponent());
setUndecorated(true);
setSize(new Dimension(480, 260));
setBackground(new Color(0, 0, 0, 0));
setLocationRelativeTo(null);
setAlwaysOnTop(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
}

private static class SplashComponent extends JComponent {
private static final Color COLOR_1 = new Color(0xFF42C9);
private static final Color COLOR_2 = new Color(0x8743FF);
private static final Color COLOR_3 = new Color(0x45CDFF);

private BufferedImage splash;
private String status;

public SplashComponent() {
setFont(createFont());
}

@Override
protected void paintComponent(Graphics g) {
if (splash == null) {
splash = createSplashTexture(getFont(), getWidth(), getHeight());
}

g.drawImage(splash, 0, 0, null);

if (status != null) {
final Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.setFont(getFont());
g2.drawString(status, 30, getHeight() - g2.getFontMetrics().getDescent() - 30);
g2.dispose();
}
}

public void setStatus(@Nullable String status) {
if (Objects.equals(status, this.status)) {
return;
}
this.status = status;
repaint();
}

@NotNull
private static BufferedImage createSplashTexture(@NotNull Font font, int width, int height) {
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

// Rounded mask
g2.setColor(Color.WHITE);
g2.fill(new RoundRectangle2D.Float(0, 0, width, height, 10, 10));
g2.setComposite(AlphaComposite.SrcAtop);

// Fancy rectangles
paintRect(g2, 285, 350, 880, 880, 155);
paintRect(g2, 200, 410, 460, 460, 10);
paintRect(g2, 450, 410, 550, 550, 30);

// Noise overlay
g2.setComposite(AlphaComposite.SrcAtop.derive(0.05f));
g2.drawImage(createNoiseTexture(width, height), 0, 0, null);

// Text
final Font font1 = font.deriveFont(36f);
final Font font2 = font.deriveFont(18f);
final FontMetrics fm1 = g2.getFontMetrics(font1);
final FontMetrics fm2 = g2.getFontMetrics(font2);

g2.setComposite(AlphaComposite.Src);
g2.setColor(Color.WHITE);
g2.setFont(font1);
g2.drawString(BuildConfig.APP_TITLE, 30, 30 + fm1.getAscent());
g2.setFont(font2);
g2.drawString(BuildConfig.APP_VERSION, 32, 30 + fm1.getHeight() + fm2.getAscent());

g2.dispose();

return image;
}

@NotNull
private static BufferedImage createNoiseTexture(int width, int height) {
final byte[] data = new byte[width * height];
new Random(0xDEC13A).nextBytes(data);

final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
final WritableRaster raster = image.getRaster();
raster.setDataElements(0, 0, width, height, data);

return image;
}

@NotNull
private static Font createFont() {
final String name;

// We can't rely on FlatLaf because it might not be initialized yet
if (SystemInfo.isWindows) {
name = "Segoe UI Light";
} else if (SystemInfo.isMacOS) {
name = "HelveticaNeue-Thin";
} else if (SystemInfo.isLinux) {
name = "SansSerif";
} else {
name = null;
}

return new Font(name, Font.PLAIN, 12);
}

private static void paintRect(@NotNull Graphics2D g, int x, int y, int width, int height, int degrees) {
g.setPaint(new LinearGradientPaint(
x - width / 2f, y,
x + height / 2f, y,
new float[]{0.0f, 0.5f, 1.0f},
new Color[]{COLOR_1, COLOR_2, COLOR_3}
));
g.rotate(Math.toRadians(degrees), x, y);
g.fillRoundRect(x - width / 2, y - height / 2, width, height, width / 2, height / 2);
g.rotate(-Math.toRadians(degrees), x, y);
g.setPaint(null);
}
}
}

0 comments on commit d2390b7

Please sign in to comment.