pre_int #4
@ -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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
tmp_chunks/chunk_00.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
tmp_chunks/chunk_01.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
tmp_chunks/chunk_02.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
tmp_chunks/chunk_03.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
tmp_chunks/chunk_04.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
tmp_chunks/chunk_05.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
tmp_chunks/chunk_06.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
tmp_chunks/chunk_07.png
Normal file
|
After Width: | Height: | Size: 22 KiB |