mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
FlatSmoothScrollingTest:
- added slider to horizontally scale chart - improved chart legend - record stack for points in chart and show in tooltip on hover
This commit is contained in:
@@ -48,7 +48,7 @@ public class FlatAnimatorTest
|
||||
|
||||
updateChartDelayedChanged();
|
||||
|
||||
lineChartPanel.setSecondWidth( 500 );
|
||||
lineChartPanel.setOneSecondWidth( 500 );
|
||||
mouseWheelTestPanel.lineChartPanel = lineChartPanel;
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ public class FlatAnimatorTest
|
||||
value = startValue + Math.round( (targetValue - startValue) * fraction );
|
||||
valueLabel.setText( String.valueOf( value ) );
|
||||
|
||||
lineChartPanel.addValue( value / (double) MAX_VALUE, Color.red );
|
||||
lineChartPanel.addValue( value / (double) MAX_VALUE, false, Color.red, null );
|
||||
}, () -> {
|
||||
targetValue = -1;
|
||||
} );
|
||||
@@ -235,7 +235,7 @@ public class FlatAnimatorTest
|
||||
// for unprecise wheels the rotation value is usually -1 or +1
|
||||
// for precise wheels the rotation value is in range ca. -10 to +10,
|
||||
// depending how fast the wheel is rotated
|
||||
lineChartPanel.addValue( 0.5 + (preciseWheelRotation / 20.), true, Color.red );
|
||||
lineChartPanel.addValue( 0.5 + (preciseWheelRotation / 20.), true, Color.red, null );
|
||||
|
||||
// increase/decrease target value if animation is in progress
|
||||
int newValue = (int) ((targetValue < 0 ? value : targetValue) + (STEP * preciseWheelRotation));
|
||||
@@ -252,7 +252,7 @@ public class FlatAnimatorTest
|
||||
value = newValue;
|
||||
valueLabel.setText( String.valueOf( value ) );
|
||||
|
||||
lineChartPanel.addValue( value / (double) MAX_VALUE, Color.red );
|
||||
lineChartPanel.addValue( value / (double) MAX_VALUE, false, Color.red, null );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,15 @@ import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.HierarchyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
@@ -37,6 +41,7 @@ import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.tree.*;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.HSLColor;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
@@ -59,8 +64,12 @@ public class FlatSmoothScrollingTest
|
||||
FlatSmoothScrollingTest() {
|
||||
initComponents();
|
||||
|
||||
oneSecondWidthChanged();
|
||||
updateChartDelayedChanged();
|
||||
|
||||
ToolTipManager.sharedInstance().setInitialDelay( 0 );
|
||||
ToolTipManager.sharedInstance().setDismissDelay( Integer.MAX_VALUE );
|
||||
|
||||
// allow enabling/disabling smooth scrolling with Alt+S without moving focus to checkbox
|
||||
registerKeyboardAction(
|
||||
e -> {
|
||||
@@ -107,8 +116,17 @@ public class FlatSmoothScrollingTest
|
||||
customScrollPane.getVerticalScrollBar().getModel().addChangeListener( new ScrollBarChangeHandler( customScrollPane, true, "custom vert", Color.pink ) );
|
||||
customScrollPane.getHorizontalScrollBar().getModel().addChangeListener( new ScrollBarChangeHandler( customScrollPane, false, "custom horz", Color.pink.darker() ) );
|
||||
|
||||
// clear chart on startup
|
||||
addHierarchyListener( e -> {
|
||||
if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && isShowing() )
|
||||
EventQueue.invokeLater( this::clearChart );
|
||||
});
|
||||
|
||||
ArrayList<String> items = new ArrayList<>();
|
||||
for( char ch = '0'; ch < 'z'; ch++ ) {
|
||||
if( (ch > '9' && ch < 'A') || (ch > 'Z' && ch < 'a') )
|
||||
continue;
|
||||
|
||||
char[] chars = new char[ch - '0' + 1];
|
||||
Arrays.fill( chars, ch );
|
||||
items.add( new String( chars ) );
|
||||
@@ -167,20 +185,47 @@ public class FlatSmoothScrollingTest
|
||||
}
|
||||
|
||||
// text components
|
||||
String text = items.stream().collect( Collectors.joining( "\n" ) );
|
||||
textArea.setText( text );
|
||||
String longText = items.stream().collect( Collectors.joining( " " ) ) + ' '
|
||||
+ items.stream().limit( 20 ).collect( Collectors.joining( " " ) );
|
||||
String text = items.stream().collect( Collectors.joining( "\n" ) ) + '\n';
|
||||
textArea.setText( longText + '\n' + text );
|
||||
textPane.setText( text );
|
||||
editorPane.setText( text );
|
||||
|
||||
textArea.select( 0, 0 );
|
||||
textPane.select( 0, 0 );
|
||||
editorPane.select( 0, 0 );
|
||||
|
||||
EventQueue.invokeLater( () -> {
|
||||
EventQueue.invokeLater( () -> {
|
||||
list.requestFocusInWindow();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
private void smoothScrollingChanged() {
|
||||
UIManager.put( "ScrollPane.smoothScrolling", smoothScrollingCheckBox.isSelected() );
|
||||
}
|
||||
|
||||
private void oneSecondWidthChanged() {
|
||||
int oneSecondWidth = oneSecondWidthSlider.getValue();
|
||||
int msPerLineX =
|
||||
oneSecondWidth <= 2000 ? 100 :
|
||||
oneSecondWidth <= 4000 ? 50 :
|
||||
oneSecondWidth <= 8000 ? 25 :
|
||||
10;
|
||||
|
||||
lineChartPanel.setOneSecondWidth( oneSecondWidth );
|
||||
lineChartPanel.setMsPerLineX( msPerLineX );
|
||||
lineChartPanel.revalidate();
|
||||
lineChartPanel.repaint();
|
||||
|
||||
if( xLabelText == null )
|
||||
xLabelText = xLabel.getText();
|
||||
xLabel.setText( MessageFormat.format( xLabelText, msPerLineX ) );
|
||||
}
|
||||
private String xLabelText;
|
||||
|
||||
private void clearChart() {
|
||||
lineChartPanel.clear();
|
||||
}
|
||||
@@ -243,7 +288,13 @@ public class FlatSmoothScrollingTest
|
||||
panel3 = new JPanel();
|
||||
chartScrollPane = new JScrollPane();
|
||||
lineChartPanel = new FlatSmoothScrollingTest.LineChartPanel();
|
||||
label1 = new JLabel();
|
||||
panel4 = new JPanel();
|
||||
xLabel = new JLabel();
|
||||
JLabel rectsLabel = new JLabel();
|
||||
JLabel yLabel = new JLabel();
|
||||
JLabel dotsLabel = new JLabel();
|
||||
JLabel oneSecondWidthLabel = new JLabel();
|
||||
oneSecondWidthSlider = new JSlider();
|
||||
updateChartDelayedCheckBox = new JCheckBox();
|
||||
clearChartButton = new JButton();
|
||||
|
||||
@@ -407,7 +458,7 @@ public class FlatSmoothScrollingTest
|
||||
// columns
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[300,grow,fill]"));
|
||||
"[100:300,grow,fill]"));
|
||||
|
||||
//======== chartScrollPane ========
|
||||
{
|
||||
@@ -420,13 +471,50 @@ public class FlatSmoothScrollingTest
|
||||
}
|
||||
add(splitPane1, "cell 0 1");
|
||||
|
||||
//---- label1 ----
|
||||
label1.setText("X: time (200ms per line) / Y: scroll bar value (10% per line)");
|
||||
add(label1, "cell 0 2");
|
||||
//======== panel4 ========
|
||||
{
|
||||
panel4.setLayout(new MigLayout(
|
||||
"insets 0,hidemode 3,gapy 0",
|
||||
// columns
|
||||
"[fill]para" +
|
||||
"[fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[]"));
|
||||
|
||||
//---- xLabel ----
|
||||
xLabel.setText("X: time ({0}ms per line)");
|
||||
panel4.add(xLabel, "cell 0 0");
|
||||
|
||||
//---- rectsLabel ----
|
||||
rectsLabel.setText("Rectangles: scrollbar values (mouse hover shows stack)");
|
||||
panel4.add(rectsLabel, "cell 1 0");
|
||||
|
||||
//---- yLabel ----
|
||||
yLabel.setText("Y: scroll bar value (10% per line)");
|
||||
panel4.add(yLabel, "cell 0 1");
|
||||
|
||||
//---- dotsLabel ----
|
||||
dotsLabel.setText("Dots: disabled blitting mode in JViewport");
|
||||
panel4.add(dotsLabel, "cell 1 1");
|
||||
}
|
||||
add(panel4, "cell 0 2");
|
||||
|
||||
//---- oneSecondWidthLabel ----
|
||||
oneSecondWidthLabel.setText("Scale X:");
|
||||
oneSecondWidthLabel.setDisplayedMnemonic('A');
|
||||
oneSecondWidthLabel.setLabelFor(oneSecondWidthSlider);
|
||||
add(oneSecondWidthLabel, "cell 0 2,alignx right,growx 0");
|
||||
|
||||
//---- oneSecondWidthSlider ----
|
||||
oneSecondWidthSlider.setMinimum(1000);
|
||||
oneSecondWidthSlider.setMaximum(10000);
|
||||
oneSecondWidthSlider.addChangeListener(e -> oneSecondWidthChanged());
|
||||
add(oneSecondWidthSlider, "cell 0 2,alignx right,growx 0,wmax 100");
|
||||
|
||||
//---- updateChartDelayedCheckBox ----
|
||||
updateChartDelayedCheckBox.setText("Update chart delayed");
|
||||
updateChartDelayedCheckBox.setMnemonic('U');
|
||||
updateChartDelayedCheckBox.setMnemonic('P');
|
||||
updateChartDelayedCheckBox.setSelected(true);
|
||||
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
|
||||
add(updateChartDelayedCheckBox, "cell 0 2,alignx right,growx 0");
|
||||
@@ -471,7 +559,9 @@ public class FlatSmoothScrollingTest
|
||||
private JPanel panel3;
|
||||
private JScrollPane chartScrollPane;
|
||||
private FlatSmoothScrollingTest.LineChartPanel lineChartPanel;
|
||||
private JLabel label1;
|
||||
private JPanel panel4;
|
||||
private JLabel xLabel;
|
||||
private JSlider oneSecondWidthSlider;
|
||||
private JCheckBox updateChartDelayedCheckBox;
|
||||
private JButton clearChartButton;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
@@ -490,7 +580,7 @@ public class FlatSmoothScrollingTest
|
||||
ScrollBarChangeHandler( DebugScrollPane scrollPane, boolean vertical, String name, Color chartColor ) {
|
||||
this.name = name;
|
||||
this.chartColor = chartColor;
|
||||
this.chartColor2 = chartColor.brighter();
|
||||
this.chartColor2 = ColorFunctions.lighten( chartColor, 0.1f );
|
||||
|
||||
// add change listener to viewport that is invoked from JViewport.setViewPosition()
|
||||
scrollPane.getViewport().addChangeListener( e -> {
|
||||
@@ -498,8 +588,15 @@ public class FlatSmoothScrollingTest
|
||||
if( vertical == scrollPane.lastScrollingWasVertical &&
|
||||
scrollPane.getViewport().getScrollMode() != JViewport.BLIT_SCROLL_MODE )
|
||||
{
|
||||
JScrollBar sb = vertical ? scrollPane.getVerticalScrollBar() : scrollPane.getHorizontalScrollBar();
|
||||
lineChartPanel.addValue( getChartValue( sb.getModel() ), true, chartColor );
|
||||
// calculate value from view position because scrollbar value is not yet up-to-date
|
||||
JViewport viewport = scrollPane.getViewport();
|
||||
Point viewPosition = viewport.getViewPosition();
|
||||
Dimension viewSize = viewport.getViewSize();
|
||||
double value = vertical
|
||||
? ((double) viewPosition.y) / (viewSize.height - viewport.getHeight())
|
||||
: ((double) viewPosition.x) / (viewSize.width - viewport.getWidth());
|
||||
|
||||
lineChartPanel.addValue( value, true, chartColor, name );
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -509,7 +606,7 @@ public class FlatSmoothScrollingTest
|
||||
DefaultBoundedRangeModel m = (DefaultBoundedRangeModel) e.getSource();
|
||||
boolean smoothScrolling = smoothScrollingCheckBox.isSelected();
|
||||
|
||||
lineChartPanel.addValue( getChartValue( m ), smoothScrolling ? chartColor : chartColor2 );
|
||||
lineChartPanel.addValue( getChartValue( m ), false, smoothScrolling ? chartColor : chartColor2, name );
|
||||
|
||||
long t = System.nanoTime() / 1000000;
|
||||
|
||||
@@ -561,6 +658,20 @@ public class FlatSmoothScrollingTest
|
||||
|
||||
super.setViewPosition( p );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g ) {
|
||||
super.paint( g );
|
||||
|
||||
if( backingStoreImage != null ) {
|
||||
System.out.println( "---------------------------------------------" );
|
||||
System.out.println( "WARNING: backingStoreImage was used for painting" );
|
||||
System.out.println( "View: " + getView() );
|
||||
System.out.println( "Clip: " + g.getClipBounds() );
|
||||
new Exception().printStackTrace( System.out );
|
||||
System.out.println( "---------------------------------------------" );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -572,25 +683,31 @@ public class FlatSmoothScrollingTest
|
||||
implements Scrollable
|
||||
{
|
||||
private static final int NEW_SEQUENCE_TIME_LAG = 500;
|
||||
private static final int NEW_SEQUENCE_GAP = 50;
|
||||
private static final int NEW_SEQUENCE_GAP = 100;
|
||||
private static final int HIT_OFFSET = 4;
|
||||
|
||||
private int secondWidth = 1000;
|
||||
private int oneSecondWidth = 1000;
|
||||
private int msPerLineX = 200;
|
||||
|
||||
private static class Data {
|
||||
final double value;
|
||||
final boolean dot;
|
||||
final long time; // in milliseconds
|
||||
final String name;
|
||||
final Exception stack;
|
||||
|
||||
Data( double value, boolean dot, long time ) {
|
||||
Data( double value, boolean dot, long time, String name, Exception stack ) {
|
||||
this.value = value;
|
||||
this.dot = dot;
|
||||
this.time = time;
|
||||
this.name = name;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// for debugging
|
||||
return String.valueOf( value );
|
||||
return "value=" + value + ", dot=" + dot + ", time=" + time + ", name=" + name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,20 +716,23 @@ public class FlatSmoothScrollingTest
|
||||
private Color lastUsedChartColor;
|
||||
private boolean updateDelayed;
|
||||
|
||||
private final List<Point> lastPoints = new ArrayList<>();
|
||||
private final List<Data> lastDatas = new ArrayList<>();
|
||||
private double lastSystemScaleFactor = 1;
|
||||
private String lastToolTipPrinted;
|
||||
|
||||
LineChartPanel() {
|
||||
int resolution = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.resolution", 10 );
|
||||
|
||||
repaintTime = new Timer( resolution * 2, e -> repaintAndRevalidate() );
|
||||
repaintTime.setRepeats( false );
|
||||
|
||||
ToolTipManager.sharedInstance().registerComponent( this );
|
||||
}
|
||||
|
||||
void addValue( double value, Color chartColor ) {
|
||||
addValue( value, false, chartColor );
|
||||
}
|
||||
|
||||
void addValue( double value, boolean dot, Color chartColor ) {
|
||||
void addValue( double value, boolean dot, Color chartColor, String name ) {
|
||||
List<Data> chartData = color2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
||||
chartData.add( new Data( value, dot, System.nanoTime() / 1000000) );
|
||||
chartData.add( new Data( value, dot, System.nanoTime() / 1000000, name, new Exception() ) );
|
||||
|
||||
lastUsedChartColor = chartColor;
|
||||
|
||||
@@ -635,8 +755,12 @@ public class FlatSmoothScrollingTest
|
||||
this.updateDelayed = updateDelayed;
|
||||
}
|
||||
|
||||
void setSecondWidth( int secondWidth ) {
|
||||
this.secondWidth = secondWidth;
|
||||
void setOneSecondWidth( int oneSecondWidth ) {
|
||||
this.oneSecondWidth = oneSecondWidth;
|
||||
}
|
||||
|
||||
void setMsPerLineX( int msPerLineX ) {
|
||||
this.msPerLineX = msPerLineX;
|
||||
}
|
||||
|
||||
private void repaintAndRevalidate() {
|
||||
@@ -670,8 +794,9 @@ public class FlatSmoothScrollingTest
|
||||
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
int secondWidth = (int) (this.secondWidth * scaleFactor);
|
||||
int oneSecondWidth = (int) (this.oneSecondWidth * scaleFactor);
|
||||
int seqGapWidth = (int) (NEW_SEQUENCE_GAP * scaleFactor);
|
||||
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * scaleFactor );
|
||||
|
||||
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
|
||||
Color lineColor2 = FlatLaf.isLafDark()
|
||||
@@ -692,51 +817,51 @@ public class FlatSmoothScrollingTest
|
||||
}
|
||||
|
||||
// paint vertical lines
|
||||
int twoHundredMillisWidth = secondWidth / 5;
|
||||
for( int i = twoHundredMillisWidth; i < width; i += twoHundredMillisWidth ) {
|
||||
g.setColor( (i % secondWidth != 0) ? lineColor : lineColor2 );
|
||||
g.drawLine( i, 0, i, height );
|
||||
int perLineXWidth = Math.round( (oneSecondWidth / 1000f) * msPerLineX );
|
||||
for( int i = 1, xv = perLineXWidth; xv < width; xv += perLineXWidth, i++ ) {
|
||||
g.setColor( (i % 5 != 0) ? lineColor : lineColor2 );
|
||||
g.drawLine( xv, 0, xv, height );
|
||||
}
|
||||
|
||||
lastPoints.clear();
|
||||
lastDatas.clear();
|
||||
lastSystemScaleFactor = scaleFactor;
|
||||
|
||||
// paint lines
|
||||
for( Map.Entry<Color, List<Data>> e : color2dataMap.entrySet() ) {
|
||||
List<Data> chartData = e.getValue();
|
||||
Color chartColor = e.getKey();
|
||||
if( FlatLaf.isLafDark() )
|
||||
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
|
||||
Color temporaryValueColor = new Color( (chartColor.getRGB() & 0xffffff) | 0x40000000, true );
|
||||
Color temporaryValueColor = ColorFunctions.fade( chartColor, FlatLaf.isLafDark() ? 0.7f : 0.3f );
|
||||
Color dataPointColor = ColorFunctions.fade( chartColor, FlatLaf.isLafDark() ? 0.6f : 0.2f );
|
||||
|
||||
// sequence start time and x coordinate
|
||||
long seqTime = 0;
|
||||
int seqX = 0;
|
||||
|
||||
// "previous" data point time, x/y coordinates and count
|
||||
long ptime = 0;
|
||||
int px = 0;
|
||||
int py = 0;
|
||||
int pcount = 0;
|
||||
int s1 = UIScale.scale( 1 );
|
||||
int s3 = UIScale.scale( 3 );
|
||||
|
||||
g.setColor( chartColor );
|
||||
|
||||
boolean first = true;
|
||||
boolean isTemporaryValue = false;
|
||||
int lastTemporaryValueIndex = -1;
|
||||
|
||||
int size = chartData.size();
|
||||
for( int i = 0; i < size; i++ ) {
|
||||
Data data = chartData.get( i );
|
||||
int dy = (int) ((height - 1) * data.value);
|
||||
|
||||
if( data.dot ) {
|
||||
int dotx = px;
|
||||
if( i > 0 && data.time > ptime + NEW_SEQUENCE_TIME_LAG )
|
||||
dotx += seqGapWidth;
|
||||
g.fillRect( dotx - s1, dy - s1, s3, s3 );
|
||||
continue;
|
||||
}
|
||||
boolean newSeq = (data.time > ptime + NEW_SEQUENCE_TIME_LAG);
|
||||
ptime = data.time;
|
||||
|
||||
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG ) {
|
||||
if( newSeq ) {
|
||||
// paint short horizontal line for previous sequence that has only one data point
|
||||
if( !first && pcount == 0 ) {
|
||||
g.drawLine( px, py, px + (int) (4 * scaleFactor), py );
|
||||
|
||||
// small vertical line to indicate data point
|
||||
g.drawLine( px, py - s1, px, py + s1 );
|
||||
g.setColor( chartColor );
|
||||
g.drawLine( px, py, px + (int) Math.round( UIScale.scale( 8 ) * scaleFactor ), py );
|
||||
}
|
||||
|
||||
// start new sequence
|
||||
@@ -745,54 +870,69 @@ public class FlatSmoothScrollingTest
|
||||
px = seqX;
|
||||
pcount = 0;
|
||||
first = false;
|
||||
} else {
|
||||
boolean isTemporaryValue = isTemporaryValue( chartData, i ) || isTemporaryValue( chartData, i - 1 );
|
||||
if( isTemporaryValue )
|
||||
g.setColor( temporaryValueColor );
|
||||
isTemporaryValue = false;
|
||||
}
|
||||
|
||||
// x/y coordinates of current data point
|
||||
int dy = (int) ((height - 1) * data.value);
|
||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth));
|
||||
|
||||
// paint rectangle to indicate data point
|
||||
g.setColor( dataPointColor );
|
||||
g.drawRect( dx - hitOffset, dy - hitOffset, hitOffset * 2, hitOffset * 2 );
|
||||
|
||||
// remember data point for tooltip
|
||||
lastPoints.add( new Point( dx, dy ) );
|
||||
lastDatas.add( data );
|
||||
|
||||
if( data.dot ) {
|
||||
int s1 = (int) Math.round( UIScale.scale( 1 ) * scaleFactor );
|
||||
int s3 = (int) Math.round( UIScale.scale( 3 ) * scaleFactor );
|
||||
g.setColor( chartColor );
|
||||
g.fillRect( dx - s1, dy - s1, s3, s3 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !newSeq ) {
|
||||
if( isTemporaryValue && i > lastTemporaryValueIndex )
|
||||
isTemporaryValue = false;
|
||||
|
||||
g.setColor( isTemporaryValue ? temporaryValueColor : chartColor );
|
||||
|
||||
// line in sequence
|
||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * secondWidth));
|
||||
g.drawLine( px, py, dx, dy );
|
||||
|
||||
if( isTemporaryValue )
|
||||
g.setColor( chartColor );
|
||||
|
||||
// small vertical lines to indicate data point
|
||||
g.drawLine( px, py - s1, px, py + s1 );
|
||||
g.drawLine( dx, dy - s1, dx, dy + s1 );
|
||||
|
||||
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.dot )
|
||||
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;
|
||||
ptime = data.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
for( List<Data> chartData : color2dataMap.values() )
|
||||
@@ -817,7 +957,7 @@ public class FlatSmoothScrollingTest
|
||||
px = seqX;
|
||||
} else {
|
||||
// line in sequence
|
||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * secondWidth));
|
||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth));
|
||||
px = dx;
|
||||
}
|
||||
|
||||
@@ -842,7 +982,7 @@ public class FlatSmoothScrollingTest
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||
return secondWidth;
|
||||
return oneSecondWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -861,6 +1001,98 @@ public class FlatSmoothScrollingTest
|
||||
public boolean getScrollableTracksViewportHeight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText( MouseEvent e ) {
|
||||
int x = (int) Math.round( e.getX() * lastSystemScaleFactor );
|
||||
int y = (int) Math.round( e.getY() * lastSystemScaleFactor );
|
||||
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * lastSystemScaleFactor );
|
||||
StringBuilder buf = null;
|
||||
|
||||
int pointsCount = lastPoints.size();
|
||||
for( int i = 0; i < pointsCount; i++ ) {
|
||||
Point pt = lastPoints.get( i );
|
||||
|
||||
// check X/Y coordinates
|
||||
if( x < pt.x - hitOffset || x > pt.x + hitOffset ||
|
||||
y < pt.y - hitOffset || y > pt.y + hitOffset )
|
||||
continue;
|
||||
|
||||
if( buf == null ) {
|
||||
buf = new StringBuilder( 5000 );
|
||||
buf.append( "<html>" );
|
||||
}
|
||||
|
||||
Data data = lastDatas.get( i );
|
||||
buf.append( "<h2>" );
|
||||
if( data.dot )
|
||||
buf.append( "DOT: " );
|
||||
buf.append( data.name ).append( ' ' ).append( data.value ).append( "</h2>" );
|
||||
|
||||
StackTraceElement[] stackTrace = data.stack.getStackTrace();
|
||||
for( int j = 0; j < stackTrace.length; j++ ) {
|
||||
StackTraceElement stackElement = stackTrace[j];
|
||||
String className = stackElement.getClassName();
|
||||
String methodName = stackElement.getMethodName();
|
||||
|
||||
// ignore methods from this class
|
||||
if( className.startsWith( FlatSmoothScrollingTest.class.getName() ) )
|
||||
continue;
|
||||
|
||||
int repeatCount = 0;
|
||||
for( int k = j + 1; k < stackTrace.length; k++ ) {
|
||||
if( !stackElement.equals( stackTrace[k] ) )
|
||||
break;
|
||||
repeatCount++;
|
||||
}
|
||||
j += repeatCount;
|
||||
|
||||
// append method
|
||||
buf.append( className )
|
||||
.append( ".<b>" )
|
||||
.append( methodName )
|
||||
.append( "</b> <span color=\"#888888\">" );
|
||||
if( stackElement.getFileName() != null ) {
|
||||
buf.append( '(' );
|
||||
buf.append( stackElement.getFileName() );
|
||||
if( stackElement.getLineNumber() >= 0 )
|
||||
buf.append( ':' ).append( stackElement.getLineNumber() );
|
||||
buf.append( ')' );
|
||||
} else
|
||||
buf.append( "(Unknown Source)" );
|
||||
buf.append( "</span>" );
|
||||
if( repeatCount > 0 )
|
||||
buf.append( " <b>" ).append( repeatCount + 1 ).append( "x</b>" );
|
||||
buf.append( "<br>" );
|
||||
|
||||
// break at some methods to make stack smaller
|
||||
if( (className.startsWith( "java.awt.event.InvocationEvent" ) && methodName.equals( "dispatch" )) ||
|
||||
(className.startsWith( "java.awt.Component" ) && methodName.equals( "processMouseWheelEvent" )) ||
|
||||
(className.startsWith( "javax.swing.JComponent" ) && methodName.equals( "processKeyBinding" )) )
|
||||
break;
|
||||
}
|
||||
buf.append( "..." );
|
||||
}
|
||||
|
||||
if( buf == null )
|
||||
return null;
|
||||
|
||||
buf.append( "<html>" );
|
||||
String toolTip = buf.toString();
|
||||
|
||||
// print to console
|
||||
if( !Objects.equals( toolTip, lastToolTipPrinted ) ) {
|
||||
lastToolTipPrinted = toolTip;
|
||||
|
||||
System.out.println( toolTip
|
||||
.replace( "<br>", "\n" )
|
||||
.replace( "<h2>", "\n---- " )
|
||||
.replace( "</h2>", " ----\n" )
|
||||
.replaceAll( "<[^>]+>", "" ) );
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class ColorIcon ----------------------------------------------------
|
||||
@@ -892,6 +1124,5 @@ public class FlatSmoothScrollingTest
|
||||
public int getIconHeight() {
|
||||
return UIScale.scale( 12 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ new FormModel {
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 3,hidemode 3"
|
||||
"$columnConstraints": "[grow,fill]"
|
||||
"$rowConstraints": "[300,grow,fill]"
|
||||
"$rowConstraints": "[100:300,grow,fill]"
|
||||
} ) {
|
||||
name: "panel3"
|
||||
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
|
||||
@@ -196,16 +196,71 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label1"
|
||||
"text": "X: time (200ms per line) / Y: scroll bar value (10% per line)"
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3,gapy 0"
|
||||
"$columnConstraints": "[fill]para[fill]"
|
||||
"$rowConstraints": "[][]"
|
||||
} ) {
|
||||
name: "panel4"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "xLabel"
|
||||
"text": "X: time ({0}ms per line)"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "rectsLabel"
|
||||
"text": "Rectangles: scrollbar values (mouse hover shows stack)"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "yLabel"
|
||||
"text": "Y: scroll bar value (10% per line)"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "dotsLabel"
|
||||
"text": "Dots: disabled blitting mode in JViewport"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "oneSecondWidthLabel"
|
||||
"text": "Scale X:"
|
||||
"displayedMnemonic": 65
|
||||
"labelFor": new FormReference( "oneSecondWidthSlider" )
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2,alignx right,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSlider" ) {
|
||||
name: "oneSecondWidthSlider"
|
||||
"minimum": 1000
|
||||
"maximum": 10000
|
||||
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "oneSecondWidthChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2,alignx right,growx 0,wmax 100"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "updateChartDelayedCheckBox"
|
||||
"text": "Update chart delayed"
|
||||
"mnemonic": 85
|
||||
"mnemonic": 80
|
||||
"selected": true
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
|
||||
Reference in New Issue
Block a user