refactor(launcher): add descriptions to demo buttons
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 15 Mar 2026 22:38:25 +0000 (00:38 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 15 Mar 2026 22:38:25 +0000 (00:38 +0200)
Redesign launcher panel with two-column layout showing button and

description for each demo. Use GroupLayout properly and frame.pack()

for automatic sizing.

src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java

index 3344454..f34148c 100644 (file)
@@ -2,7 +2,7 @@
  * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
  * This project is released under Creative Commons Zero (CC0) license.
  *
-*/
+ */
 
 package eu.svjatoslav.sixth.e3d.examples.launcher;
 
@@ -22,30 +22,91 @@ import java.awt.event.ActionEvent;
 
 /**
  * Panel containing buttons to launch each demo application.
- * Displays a vertical list of buttons organized in a sequential group layout.
+ * Displays a vertical list with button and description for each demo.
  */
 class ApplicationListPanel extends JPanel {
     private static final long serialVersionUID = 2012721856427052560L;
 
-    /** Constructs the panel with all demo launcher buttons. */
+    private static final int BUTTON_WIDTH = 160;
+    private static final int GAP = 12;
+
+    private record DemoEntry(String name, String description, AbstractAction action) {}
+
+    private static final DemoEntry[] DEMOS = {
+        new DemoEntry("My First Scene",
+                "Minimal example showing a single red box",
+                new ShowMyFirstScene()),
+        new DemoEntry("Volumetric Octree",
+                "Octree-based rendering with on-demand raytracing",
+                new ShowOctree()),
+        new DemoEntry("Mathematical graphs",
+                "Function graphs rendered in 3D around a sphere",
+                new ShowMathGraphs()),
+        new DemoEntry("Point cloud galaxy",
+                "Spiral galaxy with 10,000 glowing points",
+                new ShowPointCloud()),
+        new DemoEntry("Raining numbers",
+                "Numbers falling through 3D space like rain",
+                new ShowRain()),
+        new DemoEntry("Text editors",
+                "5x5 grid of 3D text editor components",
+                new ShowTextEditors()),
+        new DemoEntry("Text editors city",
+                "3D city of text editor panels as buildings",
+                new ShowTextEditors2()),
+        new DemoEntry("Game of Life",
+                "Conway's Game of Life with 3D visualization",
+                new ShowGameOfLife()),
+        new DemoEntry("Random polygons",
+                "1000 semi-transparent triangles with depth sorting",
+                new ShowRandomPolygons()),
+        new DemoEntry("Shaded Shapes",
+                "Shapes lit by orbiting colored light sources",
+                new ShowShadedShapes()),
+        new DemoEntry("Graphics Benchmark",
+                "Automated performance measuring FPS across modes",
+                new ShowGraphicsBenchmark()),
+    };
+
     ApplicationListPanel() {
-        final GroupLayout groupLayout = new GroupLayout(this);
-        GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
-        sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
-        sequentialGroup.addComponent(new JButton(new ShowMyFirstScene()));
-        sequentialGroup.addComponent(new JButton(new ShowOctree()));
-        sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
-        sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
-        sequentialGroup.addComponent(new JButton(new ShowRain()));
-        sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
-        sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
-        sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
-        sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
-        sequentialGroup.addComponent(new JButton(new ShowShadedShapes()));
-        sequentialGroup.addComponent(new JButton(new ShowGraphicsBenchmark()));
+        final GroupLayout layout = new GroupLayout(this);
+        setLayout(layout);
+
+        layout.setAutoCreateGaps(true);
+        layout.setAutoCreateContainerGaps(true);
+
+        final GroupLayout.SequentialGroup verticalGroup = layout.createSequentialGroup();
+        final GroupLayout.ParallelGroup horizontalButtonGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
+        final GroupLayout.ParallelGroup horizontalDescGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
+
+        final JLabel headerLabel = new JLabel("Choose an example to launch:");
+        headerLabel.setFont(headerLabel.getFont().deriveFont(java.awt.Font.BOLD));
+        verticalGroup.addComponent(headerLabel);
+        horizontalButtonGroup.addComponent(headerLabel);
+        horizontalDescGroup.addComponent(headerLabel);
+
+        for (final DemoEntry entry : DEMOS) {
+            final JButton button = new JButton(entry.action());
+            button.setPreferredSize(new java.awt.Dimension(BUTTON_WIDTH, button.getPreferredSize().height));
+
+            final JLabel descLabel = new JLabel(entry.description());
+            descLabel.setFont(descLabel.getFont().deriveFont(java.awt.Font.PLAIN));
+
+            verticalGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(button)
+                    .addComponent(descLabel));
+
+            horizontalButtonGroup.addComponent(button);
+            horizontalDescGroup.addComponent(descLabel);
+        }
+
+        layout.setVerticalGroup(verticalGroup);
+        layout.setHorizontalGroup(layout.createSequentialGroup()
+                .addGroup(horizontalButtonGroup)
+                .addGap(GAP)
+                .addGroup(horizontalDescGroup));
     }
 
-    /** Action to launch the MyFirstScene (minimal example). */
     private static class ShowMyFirstScene extends AbstractAction {
         ShowMyFirstScene() {
             putValue(NAME, "My First Scene");
@@ -57,7 +118,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the TextEditorDemo. */
     private static class ShowTextEditors extends AbstractAction {
         ShowTextEditors() {
             putValue(NAME, "Text editors");
@@ -69,7 +129,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the TextEditorDemo2 (city view). */
     private static class ShowTextEditors2 extends AbstractAction {
         ShowTextEditors2() {
             putValue(NAME, "Text editors city");
@@ -81,8 +140,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-
-    /** Action to launch the RainingNumbersDemo. */
     private static class ShowRain extends AbstractAction {
         ShowRain() {
             putValue(NAME, "Raining numbers");
@@ -94,7 +151,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the PointCloudDemo (galaxy simulation). */
     private static class ShowPointCloud extends AbstractAction {
         ShowPointCloud() {
             putValue(NAME, "Point cloud galaxy");
@@ -106,7 +162,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the GraphDemo (mathematical graphs). */
     private static class ShowMathGraphs extends AbstractAction {
         ShowMathGraphs() {
             putValue(NAME, "Mathematical graphs");
@@ -118,7 +173,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the RandomPolygonsDemo. */
     private static class ShowRandomPolygons extends AbstractAction {
         ShowRandomPolygons() {
             putValue(NAME, "Random polygons");
@@ -130,7 +184,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the OctreeDemo (volumetric rendering). */
     private static class ShowOctree extends AbstractAction {
         ShowOctree() {
             putValue(NAME, "Volumetric Octree");
@@ -142,7 +195,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the Game of Life 3D demo. */
     private static class ShowGameOfLife extends AbstractAction {
         ShowGameOfLife() {
             putValue(NAME, "Game of Life");
@@ -154,10 +206,9 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the ShadedShapesDemo (lighting demonstration). */
     private static class ShowShadedShapes extends AbstractAction {
         ShowShadedShapes() {
-            putValue(NAME, "Shaded Shapes with Lights");
+            putValue(NAME, "Shaded Shapes");
         }
 
         @Override
@@ -166,7 +217,6 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    /** Action to launch the GraphicsBenchmark. */
     private static class ShowGraphicsBenchmark extends AbstractAction {
         ShowGraphicsBenchmark() {
             putValue(NAME, "Graphics Benchmark");
@@ -177,5 +227,4 @@ class ApplicationListPanel extends JPanel {
             GraphicsBenchmark.main(null);
         }
     }
-
-}
+}
\ No newline at end of file
index 9f87947..c04d42e 100755 (executable)
@@ -38,7 +38,9 @@ class Main {
 
         frame.getContentPane().setLayout(new BorderLayout());
         frame.getContentPane().add(new ApplicationListPanel(), CENTER);
-        frame.setSize(400, 300);
+
+        frame.pack();
+        frame.setMinimumSize(frame.getSize());
 
         frame.setLocationRelativeTo(null); // center frame on screen
         frame.setVisible(true);