mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
LineChartPanel:
- support "synchron" charts - reworked to make code easier to understand/maintain - added some JavaBean properties to make it configurable in JFormDesigner - fixes some bugs removed `FlatAnimatorTest.LineChartPanel` and replaced it with `LineChartPanel`
This commit is contained in:
@@ -25,6 +25,7 @@ import java.awt.Insets;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.AbstractBorder;
|
import javax.swing.border.AbstractBorder;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
import com.formdev.flatlaf.util.AnimatedBorder;
|
import com.formdev.flatlaf.util.AnimatedBorder;
|
||||||
import com.formdev.flatlaf.util.ColorFunctions;
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
@@ -92,7 +93,7 @@ public class FlatAnimatedBorderTest
|
|||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||||
label3 = new JLabel();
|
label3 = new JLabel();
|
||||||
lineChartPanel = new FlatAnimatorTest.LineChartPanel();
|
lineChartPanel = new LineChartPanel();
|
||||||
fade1TextField = new JTextField();
|
fade1TextField = new JTextField();
|
||||||
fade1ChartColor = new FlatAnimatorTest.JChartColor();
|
fade1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||||
fade2TextField = new JTextField();
|
fade2TextField = new JTextField();
|
||||||
@@ -118,7 +119,7 @@ public class FlatAnimatedBorderTest
|
|||||||
// columns
|
// columns
|
||||||
"[fill]" +
|
"[fill]" +
|
||||||
"[fill]para" +
|
"[fill]para" +
|
||||||
"[fill]",
|
"[grow,fill]",
|
||||||
// rows
|
// rows
|
||||||
"[]" +
|
"[]" +
|
||||||
"[]" +
|
"[]" +
|
||||||
@@ -136,7 +137,7 @@ public class FlatAnimatedBorderTest
|
|||||||
//---- label3 ----
|
//---- label3 ----
|
||||||
label3.setText("Fade:");
|
label3.setText("Fade:");
|
||||||
add(label3, "cell 0 0");
|
add(label3, "cell 0 0");
|
||||||
add(lineChartPanel, "cell 2 0 1 12");
|
add(lineChartPanel, "cell 2 0 1 12,growy");
|
||||||
add(fade1TextField, "cell 0 1");
|
add(fade1TextField, "cell 0 1");
|
||||||
add(fade1ChartColor, "cell 1 1");
|
add(fade1ChartColor, "cell 1 1");
|
||||||
add(fade2TextField, "cell 0 2");
|
add(fade2TextField, "cell 0 2");
|
||||||
@@ -151,12 +152,12 @@ public class FlatAnimatedBorderTest
|
|||||||
add(material2ChartColor, "cell 1 5");
|
add(material2ChartColor, "cell 1 5");
|
||||||
|
|
||||||
//---- material3TextField ----
|
//---- material3TextField ----
|
||||||
material3TextField.putClientProperty("FlatLaf.styleClass", "large");
|
material3TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
|
||||||
add(material3TextField, "cell 0 6");
|
add(material3TextField, "cell 0 6");
|
||||||
add(material3ChartColor, "cell 1 6");
|
add(material3ChartColor, "cell 1 6");
|
||||||
|
|
||||||
//---- material4TextField ----
|
//---- material4TextField ----
|
||||||
material4TextField.putClientProperty("FlatLaf.styleClass", "large");
|
material4TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
|
||||||
add(material4TextField, "cell 0 7");
|
add(material4TextField, "cell 0 7");
|
||||||
add(material4ChartColor, "cell 1 7");
|
add(material4ChartColor, "cell 1 7");
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ public class FlatAnimatedBorderTest
|
|||||||
|
|
||||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||||
private JLabel label3;
|
private JLabel label3;
|
||||||
private FlatAnimatorTest.LineChartPanel lineChartPanel;
|
private LineChartPanel lineChartPanel;
|
||||||
private JTextField fade1TextField;
|
private JTextField fade1TextField;
|
||||||
private FlatAnimatorTest.JChartColor fade1ChartColor;
|
private FlatAnimatorTest.JChartColor fade1ChartColor;
|
||||||
private JTextField fade2TextField;
|
private JTextField fade2TextField;
|
||||||
@@ -231,7 +232,7 @@ public class FlatAnimatedBorderTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "fade" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +296,7 @@ public class FlatAnimatedBorderTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "material" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +411,7 @@ public class FlatAnimatedBorderTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
root: new FormRoot {
|
root: new FormRoot {
|
||||||
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "insets dialog,hidemode 3"
|
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||||
"$columnConstraints": "[fill][fill]para[fill]"
|
"$columnConstraints": "[fill][fill]para[grow,fill]"
|
||||||
"$rowConstraints": "[][][]para[][][][][]para[][][grow][]"
|
"$rowConstraints": "[][][]para[][][][][]para[][][grow][]"
|
||||||
} ) {
|
} ) {
|
||||||
name: "this"
|
name: "this"
|
||||||
@@ -15,10 +15,10 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 0"
|
"value": "cell 0 0"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$LineChartPanel" ) {
|
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||||
name: "lineChartPanel"
|
name: "lineChartPanel"
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 2 0 1 12"
|
"value": "cell 2 0 1 12,growy"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||||
name: "fade1TextField"
|
name: "fade1TextField"
|
||||||
@@ -122,7 +122,7 @@ new FormModel {
|
|||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"location": new java.awt.Point( 0, 0 )
|
"location": new java.awt.Point( 0, 0 )
|
||||||
"size": new java.awt.Dimension( 725, 325 )
|
"size": new java.awt.Dimension( 725, 465 )
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class FlatAnimatedIconTest
|
|||||||
private static final Color CHART_RADIO_BUTTON_3 = Color.green;
|
private static final Color CHART_RADIO_BUTTON_3 = Color.green;
|
||||||
private static final Color CHART_CHECK_BOX_1 = Color.magenta;
|
private static final Color CHART_CHECK_BOX_1 = Color.magenta;
|
||||||
private static final Color CHART_CHECK_BOX_2 = Color.orange;
|
private static final Color CHART_CHECK_BOX_2 = Color.orange;
|
||||||
private static final Color[] CHART_SWITCH_EX = new Color[] { Color.red, Color.green, Color.blue };
|
private static final Color[] CHART_SWITCH_EX = { Color.red, Color.green, Color.blue };
|
||||||
|
|
||||||
private static final String CHART_COLOR_KEY = "chartColor";
|
private static final String CHART_COLOR_KEY = "chartColor";
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ public class FlatAnimatedIconTest
|
|||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||||
radioButton1 = new JRadioButton();
|
radioButton1 = new JRadioButton();
|
||||||
radioButton1ChartColor = new FlatAnimatorTest.JChartColor();
|
radioButton1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||||
lineChartPanel = new FlatAnimatorTest.LineChartPanel();
|
lineChartPanel = new LineChartPanel();
|
||||||
radioButton2 = new JRadioButton();
|
radioButton2 = new JRadioButton();
|
||||||
radioButton2ChartColor = new FlatAnimatorTest.JChartColor();
|
radioButton2ChartColor = new FlatAnimatorTest.JChartColor();
|
||||||
radioButton3 = new JRadioButton();
|
radioButton3 = new JRadioButton();
|
||||||
@@ -114,7 +114,7 @@ public class FlatAnimatedIconTest
|
|||||||
radioButton1.setSelected(true);
|
radioButton1.setSelected(true);
|
||||||
add(radioButton1, "cell 0 0");
|
add(radioButton1, "cell 0 0");
|
||||||
add(radioButton1ChartColor, "cell 1 0");
|
add(radioButton1ChartColor, "cell 1 0");
|
||||||
add(lineChartPanel, "cell 2 0 1 7");
|
add(lineChartPanel, "cell 2 0 1 8,growy");
|
||||||
|
|
||||||
//---- radioButton2 ----
|
//---- radioButton2 ----
|
||||||
radioButton2.setText("radio 2");
|
radioButton2.setText("radio 2");
|
||||||
@@ -142,11 +142,11 @@ public class FlatAnimatedIconTest
|
|||||||
|
|
||||||
//---- durationLabel ----
|
//---- durationLabel ----
|
||||||
durationLabel.setText("Duration:");
|
durationLabel.setText("Duration:");
|
||||||
add(durationLabel, "cell 0 7 3 1");
|
add(durationLabel, "cell 0 7 2 1");
|
||||||
|
|
||||||
//---- durationField ----
|
//---- durationField ----
|
||||||
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
|
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
|
||||||
add(durationField, "cell 0 7 3 1");
|
add(durationField, "cell 0 7 2 1");
|
||||||
|
|
||||||
//---- buttonGroup1 ----
|
//---- buttonGroup1 ----
|
||||||
ButtonGroup buttonGroup1 = new ButtonGroup();
|
ButtonGroup buttonGroup1 = new ButtonGroup();
|
||||||
@@ -159,7 +159,7 @@ public class FlatAnimatedIconTest
|
|||||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||||
private JRadioButton radioButton1;
|
private JRadioButton radioButton1;
|
||||||
private FlatAnimatorTest.JChartColor radioButton1ChartColor;
|
private FlatAnimatorTest.JChartColor radioButton1ChartColor;
|
||||||
private FlatAnimatorTest.LineChartPanel lineChartPanel;
|
private LineChartPanel lineChartPanel;
|
||||||
private JRadioButton radioButton2;
|
private JRadioButton radioButton2;
|
||||||
private FlatAnimatorTest.JChartColor radioButton2ChartColor;
|
private FlatAnimatorTest.JChartColor radioButton2ChartColor;
|
||||||
private JRadioButton radioButton3;
|
private JRadioButton radioButton3;
|
||||||
@@ -216,7 +216,7 @@ public class FlatAnimatedIconTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "radio" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ public class FlatAnimatedIconTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "switch" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ public class FlatAnimatedIconTest
|
|||||||
for( int i = 0; i < animatedValues.length; i++ ) {
|
for( int i = 0; i < animatedValues.length; i++ ) {
|
||||||
float animatedValue = animatedValues[i];
|
float animatedValue = animatedValues[i];
|
||||||
if( animatedValue != 0 && animatedValue != 1 )
|
if( animatedValue != 0 && animatedValue != 1 )
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, CHART_SWITCH_EX[i] );
|
lineChartPanel.addValue( CHART_SWITCH_EX[i], animatedValue, Integer.MIN_VALUE, "switch ex" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ public class FlatAnimatedIconTest
|
|||||||
|
|
||||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||||
lineChartPanel.lineChart.addValue( animatedValue, chartColor );
|
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -22,10 +22,10 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 1 0"
|
"value": "cell 1 0"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$LineChartPanel" ) {
|
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||||
name: "lineChartPanel"
|
name: "lineChartPanel"
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 2 0 1 7"
|
"value": "cell 2 0 1 8,growy"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JRadioButton" ) {
|
add( new FormComponent( "javax.swing.JRadioButton" ) {
|
||||||
name: "radioButton2"
|
name: "radioButton2"
|
||||||
@@ -83,7 +83,7 @@ new FormModel {
|
|||||||
name: "durationLabel"
|
name: "durationLabel"
|
||||||
"text": "Duration:"
|
"text": "Duration:"
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 7 3 1"
|
"value": "cell 0 7 2 1"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||||
name: "durationField"
|
name: "durationField"
|
||||||
@@ -93,7 +93,7 @@ new FormModel {
|
|||||||
value: 200
|
value: 200
|
||||||
}
|
}
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 7 3 1"
|
"value": "cell 0 7 2 1"
|
||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"location": new java.awt.Point( 0, 0 )
|
"location": new java.awt.Point( 0, 0 )
|
||||||
|
|||||||
@@ -17,17 +17,9 @@
|
|||||||
package com.formdev.flatlaf.testing;
|
package com.formdev.flatlaf.testing;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
|
||||||
import com.formdev.flatlaf.util.Animator;
|
import com.formdev.flatlaf.util.Animator;
|
||||||
import com.formdev.flatlaf.util.CubicBezierEasing;
|
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||||
import com.formdev.flatlaf.util.HSLColor;
|
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.formdev.flatlaf.util.Animator.Interpolator;
|
import com.formdev.flatlaf.util.Animator.Interpolator;
|
||||||
import net.miginfocom.swing.*;
|
import net.miginfocom.swing.*;
|
||||||
@@ -74,7 +66,7 @@ public class FlatAnimatorTest
|
|||||||
} else {
|
} else {
|
||||||
animator = new Animator( 1000, fraction -> {
|
animator = new Animator( 1000, fraction -> {
|
||||||
scrollBar.setValue( Math.round( fraction * scrollBar.getMaximum() ) );
|
scrollBar.setValue( Math.round( fraction * scrollBar.getMaximum() ) );
|
||||||
lineChartPanel.lineChart.addValue( fraction, chartColor );
|
lineChartPanel.addValue( chartColor, fraction, Integer.MIN_VALUE, "animator" );
|
||||||
} );
|
} );
|
||||||
animator.setInterpolator( interpolator );
|
animator.setInterpolator( interpolator );
|
||||||
animator.start();
|
animator.start();
|
||||||
@@ -94,7 +86,7 @@ public class FlatAnimatorTest
|
|||||||
standardEasingChartColor = new FlatAnimatorTest.JChartColor();
|
standardEasingChartColor = new FlatAnimatorTest.JChartColor();
|
||||||
standardEasingScrollBar = new JScrollBar();
|
standardEasingScrollBar = new JScrollBar();
|
||||||
startButton = new JButton();
|
startButton = new JButton();
|
||||||
lineChartPanel = new FlatAnimatorTest.LineChartPanel();
|
lineChartPanel = new LineChartPanel();
|
||||||
|
|
||||||
//======== this ========
|
//======== this ========
|
||||||
setLayout(new MigLayout(
|
setLayout(new MigLayout(
|
||||||
@@ -159,420 +151,9 @@ public class FlatAnimatorTest
|
|||||||
private FlatAnimatorTest.JChartColor standardEasingChartColor;
|
private FlatAnimatorTest.JChartColor standardEasingChartColor;
|
||||||
private JScrollBar standardEasingScrollBar;
|
private JScrollBar standardEasingScrollBar;
|
||||||
private JButton startButton;
|
private JButton startButton;
|
||||||
private FlatAnimatorTest.LineChartPanel lineChartPanel;
|
private LineChartPanel lineChartPanel;
|
||||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||||
|
|
||||||
//---- class LineChartPanel -----------------------------------------------
|
|
||||||
|
|
||||||
static class LineChartPanel
|
|
||||||
extends JPanel
|
|
||||||
{
|
|
||||||
LineChartPanel() {
|
|
||||||
initComponents();
|
|
||||||
|
|
||||||
secondsWidthSlider.setValue( lineChart.getSecondsWidth() );
|
|
||||||
updateChartDelayedChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSecondsWidth( int secondsWidth ) {
|
|
||||||
lineChart.setSecondsWidth( secondsWidth );
|
|
||||||
secondsWidthSlider.setValue( secondsWidth );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void secondsWidthChanged() {
|
|
||||||
lineChart.setSecondsWidth( secondsWidthSlider.getValue() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateChartDelayedChanged() {
|
|
||||||
lineChart.setUpdateDelayed( updateChartDelayedCheckBox.isSelected() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearChart() {
|
|
||||||
lineChart.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initComponents() {
|
|
||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
|
||||||
JScrollPane lineChartScrollPane = new JScrollPane();
|
|
||||||
lineChart = new FlatAnimatorTest.LineChart();
|
|
||||||
JLabel lineChartInfoLabel = new JLabel();
|
|
||||||
secondsWidthSlider = new JSlider();
|
|
||||||
updateChartDelayedCheckBox = new JCheckBox();
|
|
||||||
JButton clearChartButton = new JButton();
|
|
||||||
|
|
||||||
//======== this ========
|
|
||||||
setLayout(new MigLayout(
|
|
||||||
"ltr,insets 0,hidemode 3",
|
|
||||||
// columns
|
|
||||||
"[fill]" +
|
|
||||||
"[grow,fill]",
|
|
||||||
// rows
|
|
||||||
"[400,grow,fill]" +
|
|
||||||
"[]"));
|
|
||||||
|
|
||||||
//======== lineChartScrollPane ========
|
|
||||||
{
|
|
||||||
lineChartScrollPane.putClientProperty("JScrollPane.smoothScrolling", false);
|
|
||||||
lineChartScrollPane.setViewportView(lineChart);
|
|
||||||
}
|
|
||||||
add(lineChartScrollPane, "cell 0 0 2 1");
|
|
||||||
|
|
||||||
//---- lineChartInfoLabel ----
|
|
||||||
lineChartInfoLabel.setText("X: time (500ms per line) / Y: value (10% per line)");
|
|
||||||
add(lineChartInfoLabel, "cell 0 1 2 1");
|
|
||||||
|
|
||||||
//---- secondsWidthSlider ----
|
|
||||||
secondsWidthSlider.setMinimum(100);
|
|
||||||
secondsWidthSlider.setMaximum(2000);
|
|
||||||
secondsWidthSlider.addChangeListener(e -> secondsWidthChanged());
|
|
||||||
add(secondsWidthSlider, "cell 0 1 2 1");
|
|
||||||
|
|
||||||
//---- updateChartDelayedCheckBox ----
|
|
||||||
updateChartDelayedCheckBox.setText("Update chart delayed");
|
|
||||||
updateChartDelayedCheckBox.setMnemonic('U');
|
|
||||||
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
|
|
||||||
add(updateChartDelayedCheckBox, "cell 0 1 2 1,alignx right,growx 0");
|
|
||||||
|
|
||||||
//---- clearChartButton ----
|
|
||||||
clearChartButton.setText("Clear Chart");
|
|
||||||
clearChartButton.setMnemonic('C');
|
|
||||||
clearChartButton.addActionListener(e -> clearChart());
|
|
||||||
add(clearChartButton, "cell 0 1 2 1,alignx right,growx 0");
|
|
||||||
// JFormDesigner - End of component initialization //GEN-END:initComponents
|
|
||||||
}
|
|
||||||
|
|
||||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
|
||||||
FlatAnimatorTest.LineChart lineChart;
|
|
||||||
private JSlider secondsWidthSlider;
|
|
||||||
private JCheckBox updateChartDelayedCheckBox;
|
|
||||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- class LineChart ----------------------------------------------------
|
|
||||||
|
|
||||||
static class LineChart
|
|
||||||
extends JComponent
|
|
||||||
implements Scrollable
|
|
||||||
{
|
|
||||||
private static final int NEW_SEQUENCE_TIME_LAG = 500;
|
|
||||||
private static final int NEW_SEQUENCE_GAP = 100;
|
|
||||||
|
|
||||||
private boolean asynchron;
|
|
||||||
private int secondsWidth = 500;
|
|
||||||
|
|
||||||
private static class Data {
|
|
||||||
final double value;
|
|
||||||
final boolean dot;
|
|
||||||
final Color chartColor;
|
|
||||||
final long time; // in milliseconds
|
|
||||||
|
|
||||||
Data( double value, boolean dot, Color chartColor, long time ) {
|
|
||||||
this.value = value;
|
|
||||||
this.dot = dot;
|
|
||||||
this.chartColor = chartColor;
|
|
||||||
this.time = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
// for debugging
|
|
||||||
return String.valueOf( value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<Data> syncChartData = new ArrayList<>();
|
|
||||||
private final Map<Color, List<Data>> asyncColor2dataMap = new HashMap<>();
|
|
||||||
private final Timer repaintTime;
|
|
||||||
private Color lastUsedChartColor;
|
|
||||||
private boolean updateDelayed;
|
|
||||||
|
|
||||||
LineChart() {
|
|
||||||
repaintTime = new Timer( 20, e -> repaintAndRevalidate() );
|
|
||||||
repaintTime.setRepeats( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
void enableAsynchron() {
|
|
||||||
if( !syncChartData.isEmpty() )
|
|
||||||
throw new IllegalStateException();
|
|
||||||
|
|
||||||
asynchron = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addValue( double value, Color chartColor ) {
|
|
||||||
addValue( value, false, chartColor );
|
|
||||||
}
|
|
||||||
|
|
||||||
void addValue( double value, boolean dot, Color chartColor ) {
|
|
||||||
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
|
||||||
Data data = new Data( value, dot, chartColor, System.nanoTime() / 1000000 );
|
|
||||||
if( asynchron )
|
|
||||||
chartData.add( data );
|
|
||||||
else
|
|
||||||
syncChartData.add( data );
|
|
||||||
|
|
||||||
lastUsedChartColor = chartColor;
|
|
||||||
|
|
||||||
if( updateDelayed ) {
|
|
||||||
repaintTime.stop();
|
|
||||||
repaintTime.start();
|
|
||||||
} else
|
|
||||||
repaintAndRevalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
syncChartData.clear();
|
|
||||||
asyncColor2dataMap.clear();
|
|
||||||
lastUsedChartColor = null;
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUpdateDelayed( boolean updateDelayed ) {
|
|
||||||
this.updateDelayed = updateDelayed;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getSecondsWidth() {
|
|
||||||
return secondsWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSecondsWidth( int secondsWidth ) {
|
|
||||||
this.secondsWidth = secondsWidth;
|
|
||||||
repaint();
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void repaintAndRevalidate() {
|
|
||||||
repaint();
|
|
||||||
revalidate();
|
|
||||||
|
|
||||||
// scroll horizontally
|
|
||||||
if( lastUsedChartColor != null ) {
|
|
||||||
// compute chart width of last used color and start of last sequence
|
|
||||||
int[] lastSeqX = new int[1];
|
|
||||||
int cw = chartWidth( asynchron ? asyncColor2dataMap.get( lastUsedChartColor ) : syncChartData, lastSeqX );
|
|
||||||
|
|
||||||
// scroll to end of last sequence (of last used color)
|
|
||||||
int lastSeqWidth = cw - lastSeqX[0];
|
|
||||||
int width = Math.min( lastSeqWidth, getParent().getWidth() );
|
|
||||||
int x = cw - width;
|
|
||||||
scrollRectToVisible( new Rectangle( x, 0, width, getHeight() ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void paintComponent( Graphics g ) {
|
|
||||||
Graphics g2 = g.create();
|
|
||||||
try {
|
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g2, this, this::paintImpl );
|
|
||||||
} finally {
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
|
||||||
FlatUIUtils.setRenderingHints( g );
|
|
||||||
|
|
||||||
int secondsWidth = (int) (this.secondsWidth * scaleFactor);
|
|
||||||
|
|
||||||
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
|
|
||||||
Color lineColor2 = FlatLaf.isLafDark()
|
|
||||||
? new HSLColor( lineColor ).adjustTone( 30 )
|
|
||||||
: new HSLColor( lineColor ).adjustShade( 30 );
|
|
||||||
|
|
||||||
g.translate( x, y );
|
|
||||||
|
|
||||||
// fill background
|
|
||||||
g.setColor( UIManager.getColor( "Table.background" ) );
|
|
||||||
g.fillRect( x, y, width, height );
|
|
||||||
|
|
||||||
// paint horizontal lines
|
|
||||||
for( int i = 1; i < 10; i++ ) {
|
|
||||||
int hy = (height * i) / 10;
|
|
||||||
g.setColor( (i != 5) ? lineColor : lineColor2 );
|
|
||||||
g.drawLine( 0, hy, width, hy );
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint vertical lines
|
|
||||||
int twoHundredMillisWidth = secondsWidth / 5;
|
|
||||||
for( int i = twoHundredMillisWidth; i < width; i += twoHundredMillisWidth ) {
|
|
||||||
g.setColor( (i % secondsWidth != 0) ? lineColor : lineColor2 );
|
|
||||||
g.drawLine( i, 0, i, height );
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint lines
|
|
||||||
for( Map.Entry<Color, List<Data>> e : asyncColor2dataMap.entrySet() ) {
|
|
||||||
List<Data> chartData = asynchron ? e.getValue() : syncChartData;
|
|
||||||
Color chartColor = e.getKey();
|
|
||||||
paintChartData( g, chartData, chartColor, height, scaleFactor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void paintChartData( Graphics2D g, List<Data> chartData, Color chartColor, int height, double scaleFactor ) {
|
|
||||||
if( FlatLaf.isLafDark() )
|
|
||||||
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
|
|
||||||
Color temporaryValueColor = new Color( (chartColor.getRGB() & 0xffffff) | 0x40000000, true );
|
|
||||||
|
|
||||||
int seqGapWidth = (int) (NEW_SEQUENCE_GAP * scaleFactor);
|
|
||||||
long seqTime = 0;
|
|
||||||
int seqX = 0;
|
|
||||||
long ptime = 0;
|
|
||||||
int px = 0;
|
|
||||||
int py = height - 1;
|
|
||||||
int cx = px;
|
|
||||||
int cy = py;
|
|
||||||
int pcount = 0;
|
|
||||||
|
|
||||||
g.setColor( chartColor );
|
|
||||||
|
|
||||||
boolean first = true;
|
|
||||||
int size = chartData.size();
|
|
||||||
for( int i = 0; i < size; i++ ) {
|
|
||||||
Data data = chartData.get( i );
|
|
||||||
boolean useData = (data.chartColor == chartColor);
|
|
||||||
int dy = height - 1 - (int) ((height - 1) * data.value);
|
|
||||||
|
|
||||||
if( data.dot ) {
|
|
||||||
int dotx = px;
|
|
||||||
if( i > 0 && data.time > ptime + NEW_SEQUENCE_TIME_LAG )
|
|
||||||
dotx += seqGapWidth;
|
|
||||||
int o = UIScale.scale( 1 );
|
|
||||||
int s = UIScale.scale( 3 );
|
|
||||||
g.fillRect( dotx - o, dy - o, s, s );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG ) {
|
|
||||||
if( !first && pcount == 0 )
|
|
||||||
g.drawLine( px, py, px + (int) (4 * scaleFactor), py );
|
|
||||||
|
|
||||||
// start new sequence
|
|
||||||
seqTime = data.time;
|
|
||||||
seqX = !first ? px + seqGapWidth : 0;
|
|
||||||
px = seqX;
|
|
||||||
pcount = 0;
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
boolean isTemporaryValue = isTemporaryValue( chartData, i ) || isTemporaryValue( chartData, i - 1 );
|
|
||||||
if( isTemporaryValue )
|
|
||||||
g.setColor( temporaryValueColor );
|
|
||||||
|
|
||||||
// line in sequence
|
|
||||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * secondsWidth));
|
|
||||||
if( useData )
|
|
||||||
g.drawLine( cx, cy, dx, dy );
|
|
||||||
px = dx;
|
|
||||||
pcount++;
|
|
||||||
|
|
||||||
if( isTemporaryValue )
|
|
||||||
g.setColor( chartColor );
|
|
||||||
}
|
|
||||||
|
|
||||||
py = dy;
|
|
||||||
ptime = data.time;
|
|
||||||
|
|
||||||
if( useData ) {
|
|
||||||
cx = px;
|
|
||||||
cy = py;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One or two values between two equal values are considered "temporary",
|
|
||||||
* which means that they are the target value for the following scroll animation.
|
|
||||||
*/
|
|
||||||
private boolean isTemporaryValue( List<Data> chartData, int i ) {
|
|
||||||
if( i == 0 || i == chartData.size() - 1 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Data dataBefore = chartData.get( i - 1 );
|
|
||||||
Data dataAfter = chartData.get( i + 1 );
|
|
||||||
|
|
||||||
if( dataBefore.dot || dataAfter.dot )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
double valueBefore = dataBefore.value;
|
|
||||||
double valueAfter = dataAfter.value;
|
|
||||||
|
|
||||||
return valueBefore == valueAfter ||
|
|
||||||
(i < chartData.size() - 2 && valueBefore == chartData.get( i + 2 ).value) ||
|
|
||||||
(i > 1 && chartData.get( i - 2 ).value == valueAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int chartWidth() {
|
|
||||||
int width = 0;
|
|
||||||
if( asynchron ) {
|
|
||||||
for( List<Data> chartData : asyncColor2dataMap.values() )
|
|
||||||
width = Math.max( width, chartWidth( chartData, null ) );
|
|
||||||
} else
|
|
||||||
width = Math.max( width, chartWidth( syncChartData, null ) );
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int chartWidth( List<Data> chartData, int[] lastSeqX ) {
|
|
||||||
long seqTime = 0;
|
|
||||||
int seqX = 0;
|
|
||||||
long ptime = 0;
|
|
||||||
int px = 0;
|
|
||||||
|
|
||||||
int size = chartData.size();
|
|
||||||
for( int i = 0; i < size; i++ ) {
|
|
||||||
Data data = chartData.get( i );
|
|
||||||
|
|
||||||
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG ) {
|
|
||||||
// start new sequence
|
|
||||||
seqTime = data.time;
|
|
||||||
seqX = (i > 0) ? px + NEW_SEQUENCE_GAP : 0;
|
|
||||||
px = seqX;
|
|
||||||
} else {
|
|
||||||
// line in sequence
|
|
||||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * secondsWidth));
|
|
||||||
px = dx;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptime = data.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( lastSeqX != null )
|
|
||||||
lastSeqX[0] = seqX;
|
|
||||||
|
|
||||||
return px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getPreferredSize() {
|
|
||||||
return new Dimension( chartWidth(), 200 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getPreferredScrollableViewportSize() {
|
|
||||||
return new Dimension( chartWidth(), 200 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
|
||||||
return secondsWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
|
||||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
|
|
||||||
return (viewport != null) ? viewport.getWidth() : 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getScrollableTracksViewportWidth() {
|
|
||||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
|
|
||||||
return (viewport != null) ? viewport.getWidth() > chartWidth() : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getScrollableTracksViewportHeight() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- class JChartColor --------------------------------------------------
|
//---- class JChartColor --------------------------------------------------
|
||||||
|
|
||||||
static class JChartColor
|
static class JChartColor
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -103,7 +103,7 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 3"
|
"value": "cell 0 3"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$LineChartPanel" ) {
|
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||||
name: "lineChartPanel"
|
name: "lineChartPanel"
|
||||||
auxiliary() {
|
auxiliary() {
|
||||||
"JavaCodeGenerator.variableLocal": false
|
"JavaCodeGenerator.variableLocal": false
|
||||||
@@ -115,67 +115,5 @@ new FormModel {
|
|||||||
"location": new java.awt.Point( 0, 0 )
|
"location": new java.awt.Point( 0, 0 )
|
||||||
"size": new java.awt.Dimension( 625, 625 )
|
"size": new java.awt.Dimension( 625, 625 )
|
||||||
} )
|
} )
|
||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
|
||||||
"$columnConstraints": "[fill][grow,fill]"
|
|
||||||
"$rowConstraints": "[400,grow,fill][]"
|
|
||||||
"$layoutConstraints": "ltr,insets 0,hidemode 3"
|
|
||||||
} ) {
|
|
||||||
name: "lineChartPanelNested"
|
|
||||||
auxiliary() {
|
|
||||||
"JavaCodeGenerator.className": "LineChartPanel"
|
|
||||||
}
|
|
||||||
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
|
|
||||||
name: "lineChartScrollPane"
|
|
||||||
"$client.JScrollPane.smoothScrolling": false
|
|
||||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$LineChart" ) {
|
|
||||||
name: "lineChart"
|
|
||||||
auxiliary() {
|
|
||||||
"JavaCodeGenerator.variableLocal": false
|
|
||||||
"JavaCodeGenerator.variableModifiers": 0
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
|
||||||
"value": "cell 0 0 2 1"
|
|
||||||
} )
|
|
||||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
|
||||||
name: "lineChartInfoLabel"
|
|
||||||
"text": "X: time (500ms per line) / Y: value (10% per line)"
|
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
|
||||||
"value": "cell 0 1 2 1"
|
|
||||||
} )
|
|
||||||
add( new FormComponent( "javax.swing.JSlider" ) {
|
|
||||||
name: "secondsWidthSlider"
|
|
||||||
"minimum": 100
|
|
||||||
"maximum": 2000
|
|
||||||
auxiliary() {
|
|
||||||
"JavaCodeGenerator.variableLocal": false
|
|
||||||
}
|
|
||||||
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "secondsWidthChanged", false ) )
|
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
|
||||||
"value": "cell 0 1 2 1"
|
|
||||||
} )
|
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
|
||||||
name: "updateChartDelayedCheckBox"
|
|
||||||
"text": "Update chart delayed"
|
|
||||||
"mnemonic": 85
|
|
||||||
auxiliary() {
|
|
||||||
"JavaCodeGenerator.variableLocal": false
|
|
||||||
}
|
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
|
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
|
||||||
"value": "cell 0 1 2 1,alignx right,growx 0"
|
|
||||||
} )
|
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
|
||||||
name: "clearChartButton"
|
|
||||||
"text": "Clear Chart"
|
|
||||||
"mnemonic": 67
|
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "clearChart", false ) )
|
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
|
||||||
"value": "cell 0 1 2 1,alignx right,growx 0"
|
|
||||||
} )
|
|
||||||
}, new FormLayoutConstraints( null ) {
|
|
||||||
"location": new java.awt.Point( 0, 650 )
|
|
||||||
"size": new java.awt.Dimension( 603, 325 )
|
|
||||||
} )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,33 @@ class LineChartPanel
|
|||||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isYZeroAtTop() {
|
||||||
|
return lineChart.yZeroAtTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYZeroAtTop( boolean yZeroAtTop ) {
|
||||||
|
lineChart.yZeroAtTop = yZeroAtTop;
|
||||||
|
lineChart.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAsynchron() {
|
||||||
|
return lineChart.asynchron;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAsynchron( boolean asynchron ) {
|
||||||
|
lineChart.asynchron = asynchron;
|
||||||
|
lineChart.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTemporaryValueDetection() {
|
||||||
|
return lineChart.temporaryValueDetection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemporaryValueDetection( boolean temporaryValueDetection ) {
|
||||||
|
lineChart.temporaryValueDetection = temporaryValueDetection;
|
||||||
|
lineChart.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
public String getLegendYValueText() {
|
public String getLegendYValueText() {
|
||||||
return yValueLabel.getText();
|
return yValueLabel.getText();
|
||||||
}
|
}
|
||||||
@@ -101,12 +128,21 @@ class LineChartPanel
|
|||||||
legend2Label.setText( s );
|
legend2Label.setText( s );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOneSecondWidth() {
|
||||||
|
return oneSecondWidthSlider.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOneSecondWidth( int oneSecondWidth ) {
|
||||||
|
oneSecondWidthSlider.setValue( oneSecondWidth );
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUpdateChartDelayed() {
|
public boolean isUpdateChartDelayed() {
|
||||||
return updateChartDelayedCheckBox.isSelected();
|
return updateChartDelayedCheckBox.isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUpdateChartDelayed( boolean updateChartDelayed ) {
|
public void setUpdateChartDelayed( boolean updateChartDelayed ) {
|
||||||
updateChartDelayedCheckBox.setSelected( updateChartDelayed );
|
updateChartDelayedCheckBox.setSelected( updateChartDelayed );
|
||||||
|
updateChartDelayedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addValue( Color chartColor, double value, int ivalue, String name ) {
|
void addValue( Color chartColor, double value, int ivalue, String name ) {
|
||||||
@@ -137,8 +173,8 @@ class LineChartPanel
|
|||||||
oneSecondWidth <= 8000 ? 25 :
|
oneSecondWidth <= 8000 ? 25 :
|
||||||
10;
|
10;
|
||||||
|
|
||||||
lineChart.setOneSecondWidth( oneSecondWidth );
|
lineChart.oneSecondWidth = oneSecondWidth;
|
||||||
lineChart.setMsPerLineX( msPerLineX );
|
lineChart.msPerLineX = msPerLineX;
|
||||||
lineChart.revalidate();
|
lineChart.revalidate();
|
||||||
lineChart.repaint();
|
lineChart.repaint();
|
||||||
|
|
||||||
@@ -149,7 +185,7 @@ class LineChartPanel
|
|||||||
private String xLabelText;
|
private String xLabelText;
|
||||||
|
|
||||||
private void updateChartDelayedChanged() {
|
private void updateChartDelayedChanged() {
|
||||||
lineChart.setUpdateDelayed( updateChartDelayedCheckBox.isSelected() );
|
lineChart.updateDelayed = updateChartDelayedCheckBox.isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearChart() {
|
private void clearChart() {
|
||||||
@@ -227,15 +263,17 @@ class LineChartPanel
|
|||||||
add(oneSecondWidthLabel, "cell 0 1,alignx right,growx 0");
|
add(oneSecondWidthLabel, "cell 0 1,alignx right,growx 0");
|
||||||
|
|
||||||
//---- oneSecondWidthSlider ----
|
//---- oneSecondWidthSlider ----
|
||||||
oneSecondWidthSlider.setMinimum(1000);
|
oneSecondWidthSlider.setMinimum(100);
|
||||||
oneSecondWidthSlider.setMaximum(10000);
|
oneSecondWidthSlider.setMaximum(10000);
|
||||||
|
oneSecondWidthSlider.setSnapToTicks(true);
|
||||||
|
oneSecondWidthSlider.setMajorTickSpacing(100);
|
||||||
|
oneSecondWidthSlider.setValue(500);
|
||||||
oneSecondWidthSlider.addChangeListener(e -> oneSecondWidthChanged());
|
oneSecondWidthSlider.addChangeListener(e -> oneSecondWidthChanged());
|
||||||
add(oneSecondWidthSlider, "cell 0 1,alignx right,growx 0,wmax 100");
|
add(oneSecondWidthSlider, "cell 0 1,alignx right,growx 0");
|
||||||
|
|
||||||
//---- updateChartDelayedCheckBox ----
|
//---- updateChartDelayedCheckBox ----
|
||||||
updateChartDelayedCheckBox.setText("Update chart delayed");
|
updateChartDelayedCheckBox.setText("Update chart delayed");
|
||||||
updateChartDelayedCheckBox.setMnemonic('P');
|
updateChartDelayedCheckBox.setMnemonic('P');
|
||||||
updateChartDelayedCheckBox.setSelected(true);
|
|
||||||
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
|
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
|
||||||
add(updateChartDelayedCheckBox, "cell 0 1,alignx right,growx 0");
|
add(updateChartDelayedCheckBox, "cell 0 1,alignx right,growx 0");
|
||||||
|
|
||||||
@@ -267,27 +305,36 @@ class LineChartPanel
|
|||||||
implements Scrollable
|
implements Scrollable
|
||||||
{
|
{
|
||||||
private static final int UPDATE_DELAY_MS = 20;
|
private static final int UPDATE_DELAY_MS = 20;
|
||||||
|
private static final int NEW_SEQUENCE_TIME_LAG_MS = 500;
|
||||||
private static final int NEW_SEQUENCE_TIME_LAG = 500;
|
private static final int NEW_SEQUENCE_GAP_MS = 100;
|
||||||
private static final int NEW_SEQUENCE_GAP = 100;
|
|
||||||
private static final int HIT_OFFSET = 4;
|
private static final int HIT_OFFSET = 4;
|
||||||
|
|
||||||
private int oneSecondWidth = 1000;
|
private static final boolean TEST = false;
|
||||||
private int msPerLineX = 200;
|
|
||||||
|
// asynchron means that chart for each color starts at x=0
|
||||||
|
private boolean asynchron;
|
||||||
|
private boolean temporaryValueDetection;
|
||||||
|
private boolean yZeroAtTop;
|
||||||
|
private int oneSecondWidth = 500;
|
||||||
|
private int msPerLineX = 100;
|
||||||
private final HashMap<String, String> methodHighlightMap = new HashMap<>();
|
private final HashMap<String, String> methodHighlightMap = new HashMap<>();
|
||||||
|
|
||||||
private static class Data {
|
private static class Data {
|
||||||
final double value;
|
final double value;
|
||||||
final int ivalue;
|
final int ivalue;
|
||||||
|
final Color chartColor;
|
||||||
final Color dotColor;
|
final Color dotColor;
|
||||||
final boolean dotOnly;
|
final boolean dotOnly;
|
||||||
final long time; // in milliseconds
|
final long time; // in milliseconds
|
||||||
final String name;
|
final String name;
|
||||||
final Exception stack;
|
final Exception stack;
|
||||||
|
|
||||||
Data( double value, int ivalue, Color dotColor, boolean dotOnly, long time, String name, Exception stack ) {
|
Data( double value, int ivalue, Color chartColor, Color dotColor,
|
||||||
|
boolean dotOnly, long time, String name, Exception stack )
|
||||||
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.ivalue = ivalue;
|
this.ivalue = ivalue;
|
||||||
|
this.chartColor = chartColor;
|
||||||
this.dotColor = dotColor;
|
this.dotColor = dotColor;
|
||||||
this.dotOnly = dotOnly;
|
this.dotOnly = dotOnly;
|
||||||
this.time = time;
|
this.time = time;
|
||||||
@@ -303,7 +350,8 @@ class LineChartPanel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Color, List<Data>> color2dataMap = new HashMap<>();
|
private final List<Data> syncChartData = new ArrayList<>();
|
||||||
|
private final Map<Color, List<Data>> asyncColor2dataMap = new HashMap<>();
|
||||||
private final Timer repaintTime;
|
private final Timer repaintTime;
|
||||||
private Color lastUsedChartColor;
|
private Color lastUsedChartColor;
|
||||||
private boolean updateDelayed;
|
private boolean updateDelayed;
|
||||||
@@ -318,11 +366,21 @@ class LineChartPanel
|
|||||||
repaintTime.setRepeats( false );
|
repaintTime.setRepeats( false );
|
||||||
|
|
||||||
ToolTipManager.sharedInstance().registerComponent( this );
|
ToolTipManager.sharedInstance().registerComponent( this );
|
||||||
|
|
||||||
|
if( TEST )
|
||||||
|
initTestData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addValue( Color chartColor, double value, int ivalue, Color dotColor, boolean dotOnly, String name ) {
|
void addValue( Color chartColor, double value, int ivalue, Color dotColor, boolean dotOnly, String name ) {
|
||||||
List<Data> chartData = color2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
if( TEST )
|
||||||
chartData.add( new Data( value, ivalue, dotColor, dotOnly, System.nanoTime() / 1000000, name, new Exception() ) );
|
return;
|
||||||
|
|
||||||
|
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
||||||
|
Data data = new Data( value, ivalue, chartColor, dotColor, dotOnly, System.nanoTime() / 1_000_000, name, new Exception() );
|
||||||
|
if( asynchron )
|
||||||
|
chartData.add( data );
|
||||||
|
else
|
||||||
|
syncChartData.add( data );
|
||||||
|
|
||||||
lastUsedChartColor = chartColor;
|
lastUsedChartColor = chartColor;
|
||||||
|
|
||||||
@@ -334,25 +392,19 @@ class LineChartPanel
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
color2dataMap.clear();
|
if( TEST ) {
|
||||||
|
repaint();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncChartData.clear();
|
||||||
|
asyncColor2dataMap.clear();
|
||||||
lastUsedChartColor = null;
|
lastUsedChartColor = null;
|
||||||
|
|
||||||
repaint();
|
repaint();
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUpdateDelayed( boolean updateDelayed ) {
|
|
||||||
this.updateDelayed = updateDelayed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOneSecondWidth( int oneSecondWidth ) {
|
|
||||||
this.oneSecondWidth = oneSecondWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMsPerLineX( int msPerLineX ) {
|
|
||||||
this.msPerLineX = msPerLineX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void repaintAndRevalidate() {
|
private void repaintAndRevalidate() {
|
||||||
repaint();
|
repaint();
|
||||||
revalidate();
|
revalidate();
|
||||||
@@ -361,7 +413,7 @@ class LineChartPanel
|
|||||||
if( lastUsedChartColor != null ) {
|
if( lastUsedChartColor != null ) {
|
||||||
// compute chart width of last used color and start of last sequence
|
// compute chart width of last used color and start of last sequence
|
||||||
int[] lastSeqX = new int[1];
|
int[] lastSeqX = new int[1];
|
||||||
int cw = chartWidth( color2dataMap.get( lastUsedChartColor ), lastSeqX );
|
int cw = chartWidth( asynchron ? asyncColor2dataMap.get( lastUsedChartColor ) : syncChartData, lastSeqX );
|
||||||
|
|
||||||
// scroll to end of last sequence (of last used color)
|
// scroll to end of last sequence (of last used color)
|
||||||
int lastSeqWidth = cw - lastSeqX[0];
|
int lastSeqWidth = cw - lastSeqX[0];
|
||||||
@@ -375,17 +427,17 @@ class LineChartPanel
|
|||||||
protected void paintComponent( Graphics g ) {
|
protected void paintComponent( Graphics g ) {
|
||||||
Graphics g2 = g.create();
|
Graphics g2 = g.create();
|
||||||
try {
|
try {
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g2, this, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g2, this, this::paintAt1x );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
private void paintAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
FlatUIUtils.setRenderingHints( g );
|
FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
int oneSecondWidth = (int) (UIScale.scale( this.oneSecondWidth ) * scaleFactor);
|
int oneSecondWidth = (int) (UIScale.scale( this.oneSecondWidth ) * scaleFactor);
|
||||||
int seqGapWidth = (int) (NEW_SEQUENCE_GAP * scaleFactor);
|
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
|
||||||
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * scaleFactor );
|
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * scaleFactor );
|
||||||
|
|
||||||
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
|
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
|
||||||
@@ -418,8 +470,8 @@ class LineChartPanel
|
|||||||
lastSystemScaleFactor = scaleFactor;
|
lastSystemScaleFactor = scaleFactor;
|
||||||
|
|
||||||
// paint lines
|
// paint lines
|
||||||
for( Map.Entry<Color, List<Data>> e : color2dataMap.entrySet() ) {
|
for( Map.Entry<Color, List<Data>> e : asyncColor2dataMap.entrySet() ) {
|
||||||
List<Data> chartData = e.getValue();
|
List<Data> chartData = asynchron ? e.getValue() : syncChartData;
|
||||||
Color chartColor = e.getKey();
|
Color chartColor = e.getKey();
|
||||||
if( FlatLaf.isLafDark() )
|
if( FlatLaf.isLafDark() )
|
||||||
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
|
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
|
||||||
@@ -427,107 +479,121 @@ class LineChartPanel
|
|||||||
Color dataPointColor = fade( chartColor, FlatLaf.isLafDark() ? 0.6f : 0.2f );
|
Color dataPointColor = fade( chartColor, FlatLaf.isLafDark() ? 0.6f : 0.2f );
|
||||||
|
|
||||||
// sequence start time and x coordinate
|
// sequence start time and x coordinate
|
||||||
long seqTime = 0;
|
long seqStartTime = 0;
|
||||||
int seqX = 0;
|
int seqStartX = 0;
|
||||||
|
|
||||||
// "previous" data point time, x/y coordinates and count
|
// "previous" data point time and x coordinate (used for "new sequence" detection)
|
||||||
long ptime = 0;
|
long ptime = Long.MIN_VALUE;
|
||||||
int px = 0;
|
int px = 0;
|
||||||
int py = 0;
|
|
||||||
int pcount = 0;
|
|
||||||
|
|
||||||
boolean first = true;
|
// "line" data point x/y coordinates
|
||||||
|
int lx = -1;
|
||||||
|
int ly = -1;
|
||||||
|
|
||||||
boolean isTemporaryValue = false;
|
boolean isTemporaryValue = false;
|
||||||
int lastTemporaryValueIndex = -1;
|
int lastTemporaryValueIndex = -1;
|
||||||
|
|
||||||
int size = chartData.size();
|
int size = chartData.size();
|
||||||
for( int i = 0; i < size; i++ ) {
|
for( int i = 0; i < size; i++ ) {
|
||||||
Data data = chartData.get( i );
|
Data data = chartData.get( i );
|
||||||
|
boolean useData = (data.chartColor == chartColor);
|
||||||
|
|
||||||
boolean newSeq = (data.time > ptime + NEW_SEQUENCE_TIME_LAG);
|
// start new sequence if there is a larger time gap to previous data point
|
||||||
|
boolean newSeq = (data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS);
|
||||||
ptime = data.time;
|
ptime = data.time;
|
||||||
|
|
||||||
if( newSeq ) {
|
if( newSeq ) {
|
||||||
// paint short horizontal line for previous sequence that has only one data point
|
|
||||||
if( !first && pcount == 0 ) {
|
|
||||||
g.setColor( chartColor );
|
|
||||||
g.drawLine( px, py, px + (int) Math.round( UIScale.scale( 8 ) * scaleFactor ), py );
|
|
||||||
}
|
|
||||||
|
|
||||||
// start new sequence
|
// start new sequence
|
||||||
seqTime = data.time;
|
seqStartTime = data.time;
|
||||||
seqX = !first ? px + seqGapWidth : 0;
|
seqStartX = (i > 0) ? px + seqGapWidth : 0;
|
||||||
px = seqX;
|
px = seqStartX;
|
||||||
pcount = 0;
|
lx = -1;
|
||||||
first = false;
|
ly = -1;
|
||||||
isTemporaryValue = false;
|
isTemporaryValue = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// x/y coordinates of current data point
|
// x/y coordinates of current data point
|
||||||
|
int dx = (int) (seqStartX + (((data.time - seqStartTime) / 1000.) * oneSecondWidth));
|
||||||
int dy = (int) ((height - 1) * data.value);
|
int dy = (int) ((height - 1) * data.value);
|
||||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth));
|
if( !yZeroAtTop )
|
||||||
|
dy = height - 1 - dy;
|
||||||
|
|
||||||
// paint rectangle to indicate data point
|
// remember x coordinate for "new sequence" detection
|
||||||
g.setColor( dataPointColor );
|
px = dx;
|
||||||
g.drawRect( dx - hitOffset, dy - hitOffset, hitOffset * 2, hitOffset * 2 );
|
|
||||||
|
if( !useData )
|
||||||
|
continue;
|
||||||
|
|
||||||
// remember data point for tooltip
|
// remember data point for tooltip
|
||||||
lastPoints.add( new Point( dx, dy ) );
|
lastPoints.add( new Point( dx, dy ) );
|
||||||
lastDatas.add( data );
|
lastDatas.add( data );
|
||||||
|
|
||||||
|
// paint rectangle to indicate data point
|
||||||
|
g.setColor( dataPointColor );
|
||||||
|
g.drawRect( dx - hitOffset, dy - hitOffset, hitOffset * 2, hitOffset * 2 );
|
||||||
|
|
||||||
|
// paint dot
|
||||||
if( data.dotColor != null ) {
|
if( data.dotColor != null ) {
|
||||||
int s1 = (int) Math.round( UIScale.scale( 1 ) * scaleFactor );
|
int s1 = (int) Math.round( UIScale.scale( 1 ) * scaleFactor );
|
||||||
int s3 = (int) Math.round( UIScale.scale( 3 ) * scaleFactor );
|
int s3 = (int) Math.round( UIScale.scale( 3 ) * scaleFactor );
|
||||||
g.setColor( data.dotColor );
|
g.setColor( data.dotColor );
|
||||||
g.fillRect( dx - s1, dy - s1, s3, s3 );
|
g.fillRect( dx - s1, dy - s1, s3, s3 );
|
||||||
|
|
||||||
if( data.dotOnly )
|
if( data.dotOnly )
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !newSeq ) {
|
// start of line?
|
||||||
if( isTemporaryValue && i > lastTemporaryValueIndex )
|
if( lx < 0 ) {
|
||||||
isTemporaryValue = false;
|
// remember x/y coordinates for first line
|
||||||
|
lx = dx;
|
||||||
g.setColor( isTemporaryValue ? temporaryValueColor : chartColor );
|
ly = dy;
|
||||||
|
continue;
|
||||||
// line in sequence
|
|
||||||
g.drawLine( px, py, dx, dy );
|
|
||||||
|
|
||||||
px = dx;
|
|
||||||
pcount++;
|
|
||||||
|
|
||||||
// check next data points for "temporary" value(s)
|
|
||||||
if( !isTemporaryValue ) {
|
|
||||||
// one or two values between two equal values are considered "temporary",
|
|
||||||
// which means that they are the target value for the following scroll animation
|
|
||||||
int stage = 0;
|
|
||||||
for( int j = i + 1; j < size && stage <= 2 && !isTemporaryValue; j++ ) {
|
|
||||||
Data nextData = chartData.get( j );
|
|
||||||
if( nextData.dotOnly )
|
|
||||||
continue; // ignore dots
|
|
||||||
|
|
||||||
// check whether next data point is within 10 milliseconds
|
|
||||||
if( nextData.time > data.time + 10 )
|
|
||||||
break;
|
|
||||||
|
|
||||||
if( stage >= 1 && stage <= 2 && nextData.value == data.value ) {
|
|
||||||
isTemporaryValue = true;
|
|
||||||
lastTemporaryValueIndex = j;
|
|
||||||
}
|
|
||||||
stage++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
py = dy;
|
if( isTemporaryValue && i > lastTemporaryValueIndex )
|
||||||
|
isTemporaryValue = false;
|
||||||
|
|
||||||
|
// draw line in sequence
|
||||||
|
g.setColor( isTemporaryValue ? temporaryValueColor : chartColor );
|
||||||
|
g.drawLine( lx, ly, dx, dy );
|
||||||
|
|
||||||
|
// remember x/y coordinates for next line
|
||||||
|
lx = dx;
|
||||||
|
ly = dy;
|
||||||
|
|
||||||
|
// check next data points for "temporary" value(s)
|
||||||
|
if( temporaryValueDetection && !isTemporaryValue ) {
|
||||||
|
// one or two values between two equal values are considered "temporary",
|
||||||
|
// which means that they are the target value for the following scroll animation
|
||||||
|
int stage = 0;
|
||||||
|
for( int j = i + 1; j < size && stage <= 2 && !isTemporaryValue; j++ ) {
|
||||||
|
Data nextData = chartData.get( j );
|
||||||
|
if( nextData.dotOnly )
|
||||||
|
continue; // ignore dots
|
||||||
|
|
||||||
|
// check whether next data point is within 10 milliseconds
|
||||||
|
if( nextData.time > data.time + 10 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( stage >= 1 && stage <= 2 && nextData.value == data.value ) {
|
||||||
|
isTemporaryValue = true;
|
||||||
|
lastTemporaryValueIndex = j;
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int chartWidth() {
|
private int chartWidth() {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
for( List<Data> chartData : color2dataMap.values() )
|
if( asynchron ) {
|
||||||
width = Math.max( width, chartWidth( chartData, null ) );
|
for( List<Data> chartData : asyncColor2dataMap.values() )
|
||||||
|
width = Math.max( width, chartWidth( chartData, null ) );
|
||||||
|
} else
|
||||||
|
width = Math.max( width, chartWidth( syncChartData, null ) );
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,19 +602,21 @@ class LineChartPanel
|
|||||||
int seqX = 0;
|
int seqX = 0;
|
||||||
long ptime = 0;
|
long ptime = 0;
|
||||||
int px = 0;
|
int px = 0;
|
||||||
|
int oneSecondWidth = UIScale.scale( this.oneSecondWidth );
|
||||||
|
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
|
||||||
|
|
||||||
int size = chartData.size();
|
int size = chartData.size();
|
||||||
for( int i = 0; i < size; i++ ) {
|
for( int i = 0; i < size; i++ ) {
|
||||||
Data data = chartData.get( i );
|
Data data = chartData.get( i );
|
||||||
|
|
||||||
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG ) {
|
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS ) {
|
||||||
// start new sequence
|
// start new sequence
|
||||||
seqTime = data.time;
|
seqTime = data.time;
|
||||||
seqX = (i > 0) ? px + NEW_SEQUENCE_GAP : 0;
|
seqX = (i > 0) ? px + seqGapWidth : 0;
|
||||||
px = seqX;
|
px = seqX;
|
||||||
} else {
|
} else {
|
||||||
// line in sequence
|
// line in sequence
|
||||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * UIScale.scale( oneSecondWidth )));
|
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth ));
|
||||||
px = dx;
|
px = dx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +686,11 @@ class LineChartPanel
|
|||||||
buf.append( "<h2>" );
|
buf.append( "<h2>" );
|
||||||
if( data.dotOnly )
|
if( data.dotOnly )
|
||||||
buf.append( "DOT: " );
|
buf.append( "DOT: " );
|
||||||
buf.append( data.name ).append( ' ' ).append( data.ivalue ).append( "</h2>" );
|
buf.append( data.name );
|
||||||
|
if( data.ivalue != Integer.MIN_VALUE )
|
||||||
|
buf.append( ' ' ).append( data.ivalue );
|
||||||
|
buf.append( " (" ).append( String.format( "%.3f", data.value ) ).append( ')' );
|
||||||
|
buf.append( "</h2>" );
|
||||||
|
|
||||||
StackTraceElement[] stackTrace = data.stack.getStackTrace();
|
StackTraceElement[] stackTrace = data.stack.getStackTrace();
|
||||||
for( int j = 0; j < stackTrace.length; j++ ) {
|
for( int j = 0; j < stackTrace.length; j++ ) {
|
||||||
@@ -678,6 +750,7 @@ class LineChartPanel
|
|||||||
classAndMethod.equals( "java.awt.Component.processMouseWheelEvent" ) ||
|
classAndMethod.equals( "java.awt.Component.processMouseWheelEvent" ) ||
|
||||||
classAndMethod.equals( "java.awt.Component.processMouseMotionEvent" ) ||
|
classAndMethod.equals( "java.awt.Component.processMouseMotionEvent" ) ||
|
||||||
classAndMethod.equals( "javax.swing.JComponent.processKeyBinding" ) ||
|
classAndMethod.equals( "javax.swing.JComponent.processKeyBinding" ) ||
|
||||||
|
classAndMethod.equals( "javax.swing.JComponent.paintComponent" ) ||
|
||||||
classAndMethod.equals( "com.formdev.flatlaf.util.Animator.timingEvent" ) )
|
classAndMethod.equals( "com.formdev.flatlaf.util.Animator.timingEvent" ) )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -704,6 +777,103 @@ class LineChartPanel
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initTestData() {
|
||||||
|
// asynchron = true;
|
||||||
|
|
||||||
|
addTestSimpleLine( Color.red, 0.0, "red" );
|
||||||
|
addTestSimpleLine( Color.green, 0.1, "green" );
|
||||||
|
addTestSimpleLine( Color.blue, 0.2, "blue" );
|
||||||
|
addTestSimpleLine( Color.magenta, 0.3, "magenta" );
|
||||||
|
|
||||||
|
addTestMiddleDotOnly( Color.red, 0.0, "red" );
|
||||||
|
addTestMiddleDotOnly( Color.green, 0.1, "green" );
|
||||||
|
addTestMiddleDotOnly( Color.blue, 0.2, "blue" );
|
||||||
|
addTestMiddleDotOnly( Color.magenta, 0.3, "magenta" );
|
||||||
|
|
||||||
|
addTestLeadingDotOnly( Color.red, 0.0, "red" );
|
||||||
|
addTestLeadingDotOnly( Color.green, 0.1, "green" );
|
||||||
|
addTestLeadingDotOnly( Color.blue, 0.2, "blue" );
|
||||||
|
addTestLeadingDotOnly( Color.magenta, 0.3, "magenta" );
|
||||||
|
|
||||||
|
addTestTrailingDotOnly( Color.red, 0.0, "red" );
|
||||||
|
addTestTrailingDotOnly( Color.green, 0.1, "green" );
|
||||||
|
addTestTrailingDotOnly( Color.blue, 0.2, "blue" );
|
||||||
|
addTestTrailingDotOnly( Color.magenta, 0.3, "magenta" );
|
||||||
|
|
||||||
|
addTestSingleData( Color.red, 0.0, "red" );
|
||||||
|
addTestSingleData( Color.green, 0.1, "green" );
|
||||||
|
addTestSingleData( Color.blue, 0.2, "blue" );
|
||||||
|
addTestSingleData( Color.magenta, 0.3, "magenta" );
|
||||||
|
|
||||||
|
temporaryValueDetection = true;
|
||||||
|
addTestWithTemporaryValues( Color.red, 0.0, "red" );
|
||||||
|
addTestWithTemporaryValues( Color.green, 0.1, "green" );
|
||||||
|
addTestWithTemporaryValues( Color.blue, 0.2, "blue" );
|
||||||
|
addTestWithTemporaryValues( Color.magenta, 0.3, "magenta" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestSimpleLine( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||||
|
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 50, chartColor, baseValue + 0.4, null, false, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestMiddleDotOnly( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||||
|
addTestValue( 20, chartColor, baseValue + 0.3, chartColor, true, name );
|
||||||
|
addTestValue( 30, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||||
|
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestLeadingDotOnly( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||||
|
addTestValue( 20, chartColor, baseValue + 0.0, null, false, name );
|
||||||
|
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestTrailingDotOnly( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||||
|
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||||
|
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestSingleData( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.15, chartColor, false, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestWithTemporaryValues( Color chartColor, double baseValue, String name ) {
|
||||||
|
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||||
|
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 5, chartColor, baseValue + 0.4, null, false, name );
|
||||||
|
addTestValue( 5, chartColor, baseValue + 0.1, null, false, name );
|
||||||
|
addTestValue( 40, chartColor, baseValue + 0.3, null, false, name );
|
||||||
|
testTime += 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestValue( int timeDelta, Color chartColor, double value, Color dotColor, boolean dotOnly, String name ) {
|
||||||
|
testTime += timeDelta;
|
||||||
|
|
||||||
|
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
||||||
|
Data data = new Data( value, testIValue++, chartColor, dotColor, dotOnly, testTime, name, new Exception() );
|
||||||
|
if( asynchron )
|
||||||
|
chartData.add( data );
|
||||||
|
else
|
||||||
|
syncChartData.add( data );
|
||||||
|
|
||||||
|
lastUsedChartColor = chartColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int testIValue;
|
||||||
|
private long testTime;
|
||||||
|
|
||||||
//TODO remove and use ColorFunctions.fade() when merging to main
|
//TODO remove and use ColorFunctions.fade() when merging to main
|
||||||
private static Color fade( Color color, float amount ) {
|
private static Color fade( Color color, float amount ) {
|
||||||
int newAlpha = Math.round( 255 * amount );
|
int newAlpha = Math.round( 255 * amount );
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -90,17 +90,19 @@ new FormModel {
|
|||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JSlider" ) {
|
add( new FormComponent( "javax.swing.JSlider" ) {
|
||||||
name: "oneSecondWidthSlider"
|
name: "oneSecondWidthSlider"
|
||||||
"minimum": 1000
|
"minimum": 100
|
||||||
"maximum": 10000
|
"maximum": 10000
|
||||||
|
"snapToTicks": true
|
||||||
|
"majorTickSpacing": 100
|
||||||
|
"value": 500
|
||||||
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "oneSecondWidthChanged", false ) )
|
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "oneSecondWidthChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 1,alignx right,growx 0,wmax 100"
|
"value": "cell 0 1,alignx right,growx 0"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "updateChartDelayedCheckBox"
|
name: "updateChartDelayedCheckBox"
|
||||||
"text": "Update chart delayed"
|
"text": "Update chart delayed"
|
||||||
"mnemonic": 80
|
"mnemonic": 80
|
||||||
"selected": true
|
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 1,alignx right,growx 0"
|
"value": "cell 0 1,alignx right,growx 0"
|
||||||
|
|||||||
Reference in New Issue
Block a user