diff --git a/src/main/java/vassistent/ui/AppWindow.java b/src/main/java/vassistent/ui/AppWindow.java index ba034bb..bb8f5bf 100644 --- a/src/main/java/vassistent/ui/AppWindow.java +++ b/src/main/java/vassistent/ui/AppWindow.java @@ -1,12 +1,10 @@ package vassistent.ui; -import vassistent.bootstrap.ApplicationContext; -import vassistent.controller.DashboardController; import vassistent.util.ConfigLoader; import javax.swing.*; -import javax.swing.border.Border; import java.awt.*; +import java.util.Locale; import java.util.Properties; /** @@ -14,6 +12,9 @@ import java.util.Properties; */ public class AppWindow extends JFrame { + private static final int EXPANDED_DASHBOARD_WIDTH = 460; + private static final int EXPANDED_DIVIDER_SIZE = 8; + private static final Properties config = ConfigLoader.loadProperties( "config/application.properties" @@ -22,6 +23,9 @@ public class AppWindow extends JFrame { private PixelStreamingView streamingView; private DashboardView dashboardView; private JTabbedPane tabs; + private JSplitPane driveSplitPane; + private JPanel dashboardCard; + private JToggleButton dashboardToggleButton; private JLabel mqttStatusLabel; private JLabel problemLevelLabel; @@ -33,10 +37,12 @@ public class AppWindow extends JFrame { setTitle("Virtueller Gesundheitsassistent"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setSize(1400, 850); + setMinimumSize(new Dimension(1280, 720)); + setSize(1700, 900); setLocationRelativeTo(null); + getContentPane().setBackground(new Color(15, 19, 26)); - setLayout(new BorderLayout(10,10)); + setLayout(new BorderLayout(12, 12)); String streamUrl = normalizeConfigValue( @@ -51,11 +57,13 @@ public class AppWindow extends JFrame { dashboardView = new DashboardView(); + add(createHeaderPanel(), BorderLayout.NORTH); tabs = createTabPane(); add(tabs, BorderLayout.CENTER); add(createStatusBar(), BorderLayout.SOUTH); + SwingUtilities.invokeLater(() -> setDashboardVisible(false)); - Font uiFont = new Font("Segoe UI", Font.PLAIN, 14); + Font uiFont = new Font("Segoe UI", Font.PLAIN, 16); UIManager.put("Label.font", uiFont); UIManager.put("TabbedPane.font", uiFont); } @@ -68,17 +76,96 @@ public class AppWindow extends JFrame { private JTabbedPane createTabPane() { JTabbedPane tabs = new JTabbedPane(); - tabs.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); - - JPanel streamingPanel = new JPanel(new BorderLayout(10,10)); - streamingPanel.add(streamingView, BorderLayout.CENTER); - - tabs.addTab("Avatar Streaming", streamingView); - tabs.addTab("Dashboard", dashboardView); + tabs.setBorder(BorderFactory.createEmptyBorder(0, 10, 6, 10)); + tabs.setFocusable(false); + tabs.putClientProperty("JTabbedPane.tabHeight", 38); + tabs.putClientProperty("JTabbedPane.hideTabAreaWithOneTab", true); + tabs.addTab("Drive View", createDriveViewPanel()); return tabs; } + /** + * Creates the center-display optimized drive panel. + * + * @return drive panel with large assistant stream and compact dashboard + */ + private JPanel createDriveViewPanel() { + JPanel drivePanel = new JPanel(new BorderLayout()); + drivePanel.setOpaque(false); + drivePanel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0)); + + JPanel streamCard = createCardPanel(); + streamCard.setLayout(new BorderLayout(0, 12)); + streamCard.add( + createSectionHeader( + "Virtual Health Assistant", + null + ), + BorderLayout.NORTH + ); + streamCard.add(streamingView, BorderLayout.CENTER); + + dashboardCard = createCardPanel(); + dashboardCard.setLayout(new BorderLayout(0, 12)); + dashboardCard.add( + createSectionHeader( + "Health Overview", + "Live trend and current risk level" + ), + BorderLayout.NORTH + ); + dashboardCard.add(dashboardView, BorderLayout.CENTER); + dashboardCard.setPreferredSize(new Dimension(EXPANDED_DASHBOARD_WIDTH, 0)); + dashboardCard.setMinimumSize(new Dimension(380, 0)); + + driveSplitPane = + new JSplitPane( + JSplitPane.HORIZONTAL_SPLIT, + streamCard, + dashboardCard + ); + + driveSplitPane.setBorder(null); + driveSplitPane.setOpaque(false); + driveSplitPane.setContinuousLayout(true); + driveSplitPane.setOneTouchExpandable(false); + driveSplitPane.setDividerSize(EXPANDED_DIVIDER_SIZE); + driveSplitPane.setResizeWeight(0.72); + driveSplitPane.setDividerLocation(0.72); + + drivePanel.add(driveSplitPane, BorderLayout.CENTER); + + return drivePanel; + } + + /** + * Creates a top banner with project title and context. + * + * @return header panel + */ + private JPanel createHeaderPanel() { + JPanel header = new JPanel(new BorderLayout(12, 0)); + header.setBorder(BorderFactory.createEmptyBorder(12, 16, 0, 16)); + header.setOpaque(false); + + JLabel title = new JLabel("Virtueller Gesundheitsassistent"); + title.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 30)); + title.setForeground(new Color(238, 244, 255)); + + JPanel textStack = new JPanel(); + textStack.setLayout(new BoxLayout(textStack, BoxLayout.Y_AXIS)); + textStack.setOpaque(false); + title.setAlignmentX(Component.LEFT_ALIGNMENT); + textStack.add(title); + textStack.add(Box.createVerticalStrut(4)); + + header.add(textStack, BorderLayout.WEST); + header.add(createDashboardToggleButton(), BorderLayout.EAST); + + return header; + } + /** * Creates the status bar with MQTT and problem-level indicators. * @@ -86,18 +173,18 @@ public class AppWindow extends JFrame { */ private JPanel createStatusBar() { - JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT,15,5)); + JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 12, 8)); + statusBar.setBackground(new Color(13, 16, 23)); statusBar.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createMatteBorder(1,0,0,0, Color.LIGHT_GRAY), - BorderFactory.createEmptyBorder(5,10,5,10) + BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(54, 64, 78)), + BorderFactory.createEmptyBorder(6, 12, 8, 12) )); - mqttStatusLabel = new JLabel("MQTT: Disconnected"); - problemLevelLabel = new JLabel("Problem: NONE"); + mqttStatusLabel = createStatusChip("MQTT: Disconnected"); + problemLevelLabel = createStatusChip("Problem: NONE"); statusBar.add(mqttStatusLabel); - statusBar.add(new JLabel(" | ")); statusBar.add(problemLevelLabel); return statusBar; @@ -109,8 +196,11 @@ public class AppWindow extends JFrame { * @param connected whether MQTT is currently connected */ public void updateMqttStatus(boolean connected) { - mqttStatusLabel.setText("MQTT: " + - (connected ? "Connected" : "Disconnected")); + mqttStatusLabel.setText("MQTT: " + + (connected ? "Connected" : "Disconnected")); + mqttStatusLabel.setBackground( + connected ? new Color(30, 101, 70) : new Color(108, 47, 47) + ); } /** @@ -120,6 +210,7 @@ public class AppWindow extends JFrame { */ public void updateProblemLevel(String level) { problemLevelLabel.setText("Problem: " + level); + problemLevelLabel.setBackground(getProblemChipColor(level)); } /** @@ -149,6 +240,160 @@ public class AppWindow extends JFrame { return tabs; } + /** + * Creates a panel with a subtle border and dark background. + * + * @return styled card panel + */ + private JPanel createCardPanel() { + JPanel card = new JPanel(); + card.setOpaque(true); + card.setBackground(new Color(24, 31, 41)); + card.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(61, 74, 92)), + BorderFactory.createEmptyBorder(12, 12, 12, 12) + )); + return card; + } + + /** + * Creates a section heading with title and subtitle text. + * + * @param title section title + * @param subtitle section subtitle + * @return heading panel + */ + private JPanel createSectionHeader(String title, String subtitle) { + JPanel panel = new JPanel(); + panel.setOpaque(false); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + + JLabel titleLabel = new JLabel(title); + titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + titleLabel.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 20)); + titleLabel.setForeground(new Color(233, 241, 255)); + + JLabel subtitleLabel = new JLabel(subtitle); + subtitleLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + subtitleLabel.setFont(new Font("Segoe UI", Font.PLAIN, 13)); + subtitleLabel.setForeground(new Color(143, 159, 182)); + + panel.add(titleLabel); + panel.add(Box.createVerticalStrut(2)); + panel.add(subtitleLabel); + + return panel; + } + + /** + * Creates one status label used in the bottom status bar. + * + * @param text initial status text + * @return styled status label + */ + private JLabel createStatusChip(String text) { + JLabel label = new JLabel(text); + label.setOpaque(true); + label.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 14)); + label.setForeground(new Color(237, 244, 255)); + label.setBackground(new Color(56, 66, 83)); + label.setBorder(BorderFactory.createEmptyBorder(5, 12, 5, 12)); + return label; + } + + /** + * Returns a status chip background color for the current problem level. + * + * @param level problem level name + * @return matching background color + */ + private Color getProblemChipColor(String level) { + if (level == null) { + return new Color(56, 66, 83); + } + + String normalized = level.trim().toUpperCase(Locale.ROOT); + + switch (normalized) { + case "WARNING": + return new Color(134, 104, 29); + case "HIGH": + return new Color(168, 92, 31); + case "DISASTER": + return new Color(140, 49, 49); + default: + return new Color(46, 99, 61); + } + } + + /** + * Creates the button used to toggle dashboard visibility. + * + * @return configured toggle button + */ + private JToggleButton createDashboardToggleButton() { + dashboardToggleButton = new JToggleButton("Dashboard einblenden"); + dashboardToggleButton.setFocusable(false); + dashboardToggleButton.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 14)); + dashboardToggleButton.setForeground(new Color(233, 241, 255)); + dashboardToggleButton.setBackground(new Color(39, 77, 57)); + dashboardToggleButton.setBorder(BorderFactory.createEmptyBorder(8, 14, 8, 14)); + dashboardToggleButton.addActionListener( + event -> setDashboardVisible(dashboardToggleButton.isSelected()) + ); + return dashboardToggleButton; + } + + /** + * Shows or hides the dashboard side panel in drive view. + * + * @param visible whether dashboard should be visible + */ + private void setDashboardVisible(boolean visible) { + if (driveSplitPane == null || dashboardCard == null || dashboardToggleButton == null) { + return; + } + + dashboardCard.setVisible(visible); + driveSplitPane.setDividerSize(visible ? EXPANDED_DIVIDER_SIZE : 0); + + if (visible) { + dashboardToggleButton.setSelected(true); + dashboardToggleButton.setText("Dashboard ausblenden"); + dashboardToggleButton.setBackground(new Color(69, 86, 109)); + + SwingUtilities.invokeLater(() -> { + int width = driveSplitPane.getWidth(); + if (width <= 0) { + driveSplitPane.setDividerLocation(0.72); + return; + } + + int dividerLocation = Math.max( + (int) (width * 0.55), + width - EXPANDED_DASHBOARD_WIDTH + ); + driveSplitPane.setDividerLocation(dividerLocation); + }); + } else { + dashboardToggleButton.setSelected(false); + dashboardToggleButton.setText("Dashboard einblenden"); + dashboardToggleButton.setBackground(new Color(39, 77, 57)); + + SwingUtilities.invokeLater(() -> { + int width = driveSplitPane.getWidth(); + if (width > 0) { + driveSplitPane.setDividerLocation(width); + } else { + driveSplitPane.setDividerLocation(1.0d); + } + }); + } + + driveSplitPane.revalidate(); + driveSplitPane.repaint(); + } + /** * Trims a config value and strips optional wrapping quotes. * diff --git a/src/main/java/vassistent/ui/DashboardView.java b/src/main/java/vassistent/ui/DashboardView.java index 4ab9c42..78da4d7 100644 --- a/src/main/java/vassistent/ui/DashboardView.java +++ b/src/main/java/vassistent/ui/DashboardView.java @@ -12,18 +12,15 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.TextAnchor; -import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.time.Millisecond; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; -import vassistent.model.ProblemLevel; import vassistent.model.RatioPoint; import javax.swing.*; import java.awt.*; -import java.sql.Timestamp; +import java.text.SimpleDateFormat; import java.time.ZoneId; -import java.util.Arrays; import java.util.Date; import java.util.List; @@ -42,28 +39,30 @@ public class DashboardView extends JPanel { * Creates the dashboard panel with problem-level bar and time-series chart. */ public DashboardView() { - setLayout(new BorderLayout()); - setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + setLayout(new BorderLayout(0, 12)); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); setOpaque(false); - JPanel card = new JPanel(new BorderLayout(10,10)); - card.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); - - card.setBackground(UIManager.getColor("Panel.background")); + JPanel card = new JPanel(new BorderLayout(0, 14)); + card.setBackground(new Color(22, 28, 37)); card.setOpaque(true); - card.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( - UIManager.getColor("Component.borderColor") + new Color(58, 72, 90) ), - BorderFactory.createEmptyBorder(12,12,12,12) + BorderFactory.createEmptyBorder(14, 14, 14, 14) )); // ---------- TOP: Problem Level ---------- - JPanel topWrapper = new JPanel(new BorderLayout()); - topWrapper.setBorder(BorderFactory.createEmptyBorder(0,0,10,0)); + JPanel topWrapper = new JPanel(new BorderLayout(0, 8)); + topWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); topWrapper.setOpaque(false); + JLabel topLabel = new JLabel("Current Risk Level"); + topLabel.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 24)); + topLabel.setForeground(new Color(230, 237, 249)); + topWrapper.add(topLabel, BorderLayout.NORTH); + levelBar = new ProblemLevelBar(); topWrapper.add(levelBar, BorderLayout.CENTER); @@ -83,21 +82,28 @@ public class DashboardView extends JPanel { ); XYPlot plot = chart.getXYPlot(); + chart.setBackgroundPaint(new Color(22, 28, 37)); + chart.getTitle().setPaint(new Color(222, 231, 245)); + chart.getTitle().setFont(new Font("Segoe UI Semibold", Font.PLAIN, 16)); long tenMinutes = 10 * 60 * 1000L; DateAxis domainAxis = (DateAxis) plot.getDomainAxis(); - domainAxis.setFixedAutoRange(tenMinutes); domainAxis.setAutoRange(true); + domainAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm:ss")); + domainAxis.setLabelPaint(new Color(170, 185, 208)); + domainAxis.setTickLabelPaint(new Color(170, 185, 208)); + domainAxis.setTickMarkPaint(new Color(89, 105, 128)); XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); renderer.setDefaultShapesVisible(false); + renderer.setSeriesPaint(0, new Color(67, 175, 255)); renderer.setSeriesStroke(0, new BasicStroke( - 2.5f, + 3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); @@ -110,14 +116,25 @@ public class DashboardView extends JPanel { chart.getXYPlot().getRangeAxis().setRange(0.0, 1.02); NumberAxis rangeAxis = (NumberAxis) chart.getXYPlot().getRangeAxis(); rangeAxis.setTickUnit(new NumberTickUnit(0.1)); + rangeAxis.setLabelPaint(new Color(170, 185, 208)); + rangeAxis.setTickLabelPaint(new Color(170, 185, 208)); + rangeAxis.setTickMarkPaint(new Color(89, 105, 128)); + + plot.setBackgroundPaint(new Color(16, 20, 27)); + plot.setOutlineVisible(false); + plot.setRangeGridlinePaint(new Color(89, 105, 128, 120)); + plot.setDomainGridlinePaint(new Color(89, 105, 128, 120)); chart.setAntiAlias(true); chart.setTextAntiAlias(true); - chart.getPlot().setBackgroundPaint(new Color(245,245,245)); - chart.getPlot().setOutlineVisible(false); ChartPanel chartPanel = new ChartPanel(chart); - chartPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + chartPanel.setOpaque(false); + chartPanel.setBackground(new Color(16, 20, 27)); + chartPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(58, 72, 90)), + BorderFactory.createEmptyBorder(8, 8, 8, 8) + )); card.add(chartPanel, BorderLayout.CENTER); @@ -188,7 +205,7 @@ public class DashboardView extends JPanel { ValueMarker marker = new ValueMarker(value); - marker.setPaint(new Color(150,150,150,150)); + marker.setPaint(getThresholdColor(label)); float[] dash = {6.0f, 6.0f}; marker.setStroke(new BasicStroke( @@ -201,12 +218,31 @@ public class DashboardView extends JPanel { )); marker.setLabel(label); - marker.setLabelFont(new Font("Segoe UI", Font.PLAIN, 11)); - marker.setLabelPaint(new Color(160,160,160)); + marker.setLabelFont(new Font("Segoe UI Semibold", Font.PLAIN, 12)); + marker.setLabelPaint(new Color(188, 203, 223)); marker.setLabelAnchor(RectangleAnchor.RIGHT); marker.setLabelTextAnchor(TextAnchor.TOP_RIGHT); plot.addRangeMarker(marker, Layer.BACKGROUND); } + + /** + * Returns a distinct marker color for each threshold level. + * + * @param label threshold label + * @return translucent marker color + */ + private Color getThresholdColor(String label) { + switch (label) { + case "Warning": + return new Color(255, 210, 69, 165); + case "High": + return new Color(255, 153, 65, 170); + case "Disaster": + return new Color(255, 92, 92, 175); + default: + return new Color(168, 178, 194, 140); + } + } } diff --git a/src/main/java/vassistent/ui/PixelStreamingView.java b/src/main/java/vassistent/ui/PixelStreamingView.java index 5b884fb..a8d9065 100644 --- a/src/main/java/vassistent/ui/PixelStreamingView.java +++ b/src/main/java/vassistent/ui/PixelStreamingView.java @@ -8,8 +8,6 @@ import org.cef.handler.CefAppHandlerAdapter; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; /** * Embedded JCEF browser panel used to display the avatar pixel stream. @@ -31,7 +29,9 @@ public class PixelStreamingView extends JPanel { */ public PixelStreamingView(String startURL, boolean useOSR, boolean isTransparent) { super(new BorderLayout()); - + setOpaque(true); + setBackground(new Color(8, 11, 16)); + CefApp.addAppHandler(new CefAppHandlerAdapter(null) { /** * Handles JCEF application state transitions. @@ -55,7 +55,16 @@ public class PixelStreamingView extends JPanel { browser = client.createBrowser(startURL, useOSR, isTransparent); browserUI_ = browser.getUIComponent(); - add(browserUI_, BorderLayout.CENTER); + JPanel browserContainer = new JPanel(new BorderLayout()); + browserContainer.setOpaque(true); + browserContainer.setBackground(new Color(6, 9, 14)); + browserContainer.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(56, 68, 86)), + BorderFactory.createEmptyBorder(6, 6, 6, 6) + )); + browserContainer.add(browserUI_, BorderLayout.CENTER); + + add(browserContainer, BorderLayout.CENTER); } /** diff --git a/src/main/java/vassistent/ui/ProblemLevelBar.java b/src/main/java/vassistent/ui/ProblemLevelBar.java index ac23172..f23c753 100644 --- a/src/main/java/vassistent/ui/ProblemLevelBar.java +++ b/src/main/java/vassistent/ui/ProblemLevelBar.java @@ -17,9 +17,10 @@ public class ProblemLevelBar extends JPanel { * Creates the level bar component with preferred sizing and transparent background. */ public ProblemLevelBar() { - setPreferredSize(new Dimension(100, 50)); + setPreferredSize(new Dimension(420, 88)); + setMinimumSize(new Dimension(320, 72)); setOpaque(false); - setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); } /** @@ -66,47 +67,45 @@ public class ProblemLevelBar extends JPanel { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - // --- Hintergrund --- - GradientPaint bg = new GradientPaint( - 0, 0, new Color(235,235,235), - 0, height, new Color(210,210,210) + int arc = Math.max(22, height / 2); + + GradientPaint trackGradient = new GradientPaint( + 0, 0, new Color(51, 60, 76), + 0, height, new Color(29, 36, 48) ); + g2.setPaint(trackGradient); + g2.fillRoundRect(0, 0, width, height, arc, arc); - g2.setPaint(bg); - g2.fillRoundRect(0, 0, width, height, 20, 20); - - // --- Gefüllter Bereich --- int filledWidth = (int) (width * ratio); + if (filledWidth > 0) { + filledWidth = Math.max(filledWidth, arc / 2); + } Color baseColor = getColorForLevel(level); - Color darkerColor = baseColor.darker(); - - GradientPaint gradient = new GradientPaint( - 0, 0, darkerColor, - filledWidth, 0, baseColor + GradientPaint fillGradient = new GradientPaint( + 0, 0, baseColor.darker(), + Math.max(1, filledWidth), 0, baseColor ); - - g2.setPaint(gradient); - g2.fillRoundRect(0, 0, filledWidth, height, 20, 20); + g2.setPaint(fillGradient); + g2.fillRoundRect(0, 0, filledWidth, height, arc, arc); g2.setStroke(new BasicStroke(2f)); - g2.setColor(baseColor.brighter()); - g2.drawRoundRect(1,1,width-2,height-2,20,20); + g2.setColor(new Color(175, 190, 212)); + g2.drawRoundRect(1, 1, width - 2, height - 2, arc, arc); - // --- Text --- - String text = level.name() + " (" + (int)(ratio * 100) + "%)"; - - g2.setColor(Color.BLACK); + String text = "Problem " + level.name() + " " + (int) (ratio * 100) + "%"; + g2.setFont(new Font("Segoe UI Semibold", + Font.PLAIN, + Math.max(19, (int) (height * 0.33)))); + g2.setColor(new Color(244, 248, 255)); FontMetrics fm = g2.getFontMetrics(); - int textWidth = fm.stringWidth(text); - - g2.drawString( - text, - (width - textWidth) / 2, - (height + fm.getAscent()) / 2 - 3 - ); + int textX = (width - fm.stringWidth(text)) / 2; + int textY = (height - fm.getHeight()) / 2 + fm.getAscent(); + g2.drawString(text, textX, textY); } /** @@ -118,13 +117,13 @@ public class ProblemLevelBar extends JPanel { private Color getColorForLevel(ProblemLevel level) { switch (level) { case DISASTER: - return new Color(200, 0, 0); + return new Color(214, 70, 70); case HIGH: - return new Color(255, 140, 0); + return new Color(234, 134, 45); case WARNING: - return new Color(255, 215, 0); + return new Color(219, 180, 52); default: - return new Color(0, 170, 0); + return new Color(53, 178, 107); } } } diff --git a/tmp_chunks/chunk_00.png b/tmp_chunks/chunk_00.png new file mode 100644 index 0000000..ad41a62 Binary files /dev/null and b/tmp_chunks/chunk_00.png differ diff --git a/tmp_chunks/chunk_01.png b/tmp_chunks/chunk_01.png new file mode 100644 index 0000000..7d3795d Binary files /dev/null and b/tmp_chunks/chunk_01.png differ diff --git a/tmp_chunks/chunk_02.png b/tmp_chunks/chunk_02.png new file mode 100644 index 0000000..76fbcc9 Binary files /dev/null and b/tmp_chunks/chunk_02.png differ diff --git a/tmp_chunks/chunk_03.png b/tmp_chunks/chunk_03.png new file mode 100644 index 0000000..362faa8 Binary files /dev/null and b/tmp_chunks/chunk_03.png differ diff --git a/tmp_chunks/chunk_04.png b/tmp_chunks/chunk_04.png new file mode 100644 index 0000000..6645f15 Binary files /dev/null and b/tmp_chunks/chunk_04.png differ diff --git a/tmp_chunks/chunk_05.png b/tmp_chunks/chunk_05.png new file mode 100644 index 0000000..8ce9373 Binary files /dev/null and b/tmp_chunks/chunk_05.png differ diff --git a/tmp_chunks/chunk_06.png b/tmp_chunks/chunk_06.png new file mode 100644 index 0000000..816e45d Binary files /dev/null and b/tmp_chunks/chunk_06.png differ diff --git a/tmp_chunks/chunk_07.png b/tmp_chunks/chunk_07.png new file mode 100644 index 0000000..792aa9b Binary files /dev/null and b/tmp_chunks/chunk_07.png differ