diff --git a/CHANGELOG.md b/CHANGELOG.md index 880ec9b2..dfc9e4fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ FlatLaf Change Log #### New features and improvements +- Slider: Clicking on track now immediately moves the thumb to mouse location + and starts dragging the thumb. - Extras: Added standard component extension classes that provides easy access to FlatLaf specific client properties (see package `com.formdev.flatlaf.extras.components`). diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java index b67281a9..300f0e7c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java @@ -226,6 +226,10 @@ public class FlatSliderUI g.drawRect( trackRect.x, trackRect.y, trackRect.width - 1, trackRect.height - 1 ); g.setColor( Color.red ); g.drawRect( thumbRect.x, thumbRect.y, thumbRect.width - 1, thumbRect.height - 1 ); + g.setColor( Color.green ); + g.drawRect( tickRect.x, tickRect.y, tickRect.width - 1, tickRect.height - 1 ); + g.setColor( Color.red ); + g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 ); debug*/ super.paint( g, c ); @@ -502,7 +506,47 @@ debug*/ @Override public void mousePressed( MouseEvent e ) { setThumbPressed( isOverThumb( e ) ); + + if( !slider.isEnabled() ) + return; + + // use "old" behavior when clicking on track + if( UIManager.getBoolean( "Slider.scrollOnTrackClick" ) ) { + super.mousePressed( e ); + return; + } + + // "new" behavior set thumb to mouse location when clicking on track + + int x = e.getX(); + int y = e.getY(); + + // clicked on thumb --> let super class do the work + calculateGeometry(); + if( thumbRect.contains( x, y ) ) { + super.mousePressed( e ); + return; + } + + if( UIManager.getBoolean( "Slider.onlyLeftMouseButtonDrag" ) && + !SwingUtilities.isLeftMouseButton( e ) ) + return; + + // move the mouse event coordinates to the center of the thumb + int tx = thumbRect.x + (thumbRect.width / 2) - x; + int ty = thumbRect.y + (thumbRect.height / 2) - y; + e.translatePoint( tx, ty ); + + // invoke super mousePressed() to start dragging thumb super.mousePressed( e ); + + // move the mouse event coordinates back to current mouse location + e.translatePoint( -tx, -ty ); + + // invoke super mouseDragged() to update thumb location + super.mouseDragged( e ); + + setThumbPressed( true ); } @Override diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java index 5cee1105..ced0367e 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java @@ -37,10 +37,18 @@ public class FlatComponentsTest } ); } + private final JSlider[] allSliders; + private final JSlider[] roundSliders; + private final JSlider[] directionalSliders; + FlatComponentsTest() { initComponents(); buttonTypeComboBox.init( ButtonType.class, true ); + + allSliders = new JSlider[] { slider1, slider2, slider3, slider4, slider5, slider6 }; + roundSliders = new JSlider[] { slider1, slider2, slider6 }; + directionalSliders = new JSlider[] { slider3, slider4, slider5 }; } private void changeProgress() { @@ -152,6 +160,64 @@ public class FlatComponentsTest textField1.requestFocusInWindow(); } + private void sliderPaintTrackChanged() { + boolean paintTrack = sliderPaintTrackCheckBox.isSelected(); + for( JSlider slider : allSliders ) + slider.setPaintTrack( paintTrack ); + } + + private void sliderPaintTicksChanged() { + Boolean paintTicks = sliderPaintTicksCheckBox.getChecked(); + if( paintTicks != null ) { + for( JSlider slider : allSliders ) + slider.setPaintTicks( paintTicks ); + } else { + for( JSlider slider : roundSliders ) + slider.setPaintTicks( false ); + for( JSlider slider : directionalSliders ) + slider.setPaintTicks( true ); + } + } + + private void sliderPaintLabelsChanged() { + Boolean paintLabels = sliderPaintLabelsCheckBox.getChecked(); + if( paintLabels != null ) { + for( JSlider slider : allSliders ) + slider.setPaintLabels( paintLabels ); + } else { + for( JSlider slider : roundSliders ) + slider.setPaintLabels( false ); + for( JSlider slider : directionalSliders ) + slider.setPaintLabels( true ); + } + } + + private void sliderInvertedChanged() { + boolean inverted = sliderInvertedCheckBox.isSelected(); + for( JSlider slider : allSliders ) + slider.setInverted( inverted ); + } + + private void sliderSnapToTicksChanged() { + boolean snapToTicks = sliderSnapToTicksCheckBox.isSelected(); + for( JSlider slider : allSliders ) + slider.setSnapToTicks( snapToTicks ); + } + + private void majorThickSpacingChanged() { + int majorTickSpacing = (Integer) majorTickSpacingSpinner.getValue(); + for( JSlider slider : directionalSliders ) { + slider.setLabelTable( null ); + slider.setMajorTickSpacing( majorTickSpacing ); + } + } + + private void minorThickSpacingChanged() { + int minorTickSpacing = (Integer) minorTickSpacingSpinner.getValue(); + for( JSlider slider : directionalSliders ) + slider.setMinorTickSpacing( minorTickSpacing ); + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents JLabel labelLabel = new JLabel(); @@ -273,8 +339,8 @@ public class FlatComponentsTest FlatScrollBar scrollBar7 = new FlatScrollBar(); FlatScrollBar scrollBar8 = new FlatScrollBar(); JSeparator separator2 = new JSeparator(); - JSlider slider2 = new JSlider(); - JSlider slider4 = new JSlider(); + slider2 = new JSlider(); + slider4 = new JSlider(); JScrollPane scrollPane14 = new JScrollPane(); progressBar3 = new FlatProgressBar(); progressBar4 = new FlatProgressBar(); @@ -307,20 +373,29 @@ public class FlatComponentsTest JSeparator separator1 = new JSeparator(); JPanel panel2 = new JPanel(); JLabel sliderLabel = new JLabel(); - JSlider slider1 = new JSlider(); - JSlider slider6 = new JSlider(); + slider1 = new JSlider(); + slider6 = new JSlider(); + JPanel panel6 = new JPanel(); + sliderPaintTrackCheckBox = new JCheckBox(); + sliderPaintTicksCheckBox = new FlatTriStateCheckBox(); + sliderPaintLabelsCheckBox = new FlatTriStateCheckBox(); + sliderInvertedCheckBox = new JCheckBox(); + sliderSnapToTicksCheckBox = new JCheckBox(); + majorTickSpacingSpinner = new JSpinner(); + minorTickSpacingSpinner = new JSpinner(); JLabel sliderLabel2 = new JLabel(); slider3 = new JSlider(); - JSlider slider5 = new JSlider(); + slider5 = new JSlider(); JLabel progressBarLabel = new JLabel(); progressBar1 = new FlatProgressBar(); progressBar2 = new FlatProgressBar(); + JPanel panel7 = new JPanel(); indeterminateCheckBox = new JCheckBox(); squareCheckBox = new JCheckBox(); + largeHeightCheckBox = new JCheckBox(); JLabel toolTipLabel = new JLabel(); JToolTip toolTip1 = new JToolTip(); JToolTip toolTip2 = new JToolTip(); - largeHeightCheckBox = new JCheckBox(); JLabel toolBarLabel = new JLabel(); JToolBar toolBar1 = new JToolBar(); JButton button4 = new JButton(); @@ -1227,6 +1302,56 @@ public class FlatComponentsTest slider6.setValue(30); add(slider6, "cell 1 19 3 1"); + //======== panel6 ======== + { + panel6.setBorder(new TitledBorder("JSlider Control")); + panel6.setLayout(new MigLayout( + "ltr,insets 0,hidemode 3", + // columns + "[]", + // rows + "[]0" + + "[]0" + + "[]")); + + //---- sliderPaintTrackCheckBox ---- + sliderPaintTrackCheckBox.setText("track"); + sliderPaintTrackCheckBox.setSelected(true); + sliderPaintTrackCheckBox.addActionListener(e -> sliderPaintTrackChanged()); + panel6.add(sliderPaintTrackCheckBox, "cell 0 0"); + + //---- sliderPaintTicksCheckBox ---- + sliderPaintTicksCheckBox.setText("ticks"); + sliderPaintTicksCheckBox.addActionListener(e -> sliderPaintTicksChanged()); + panel6.add(sliderPaintTicksCheckBox, "cell 0 0"); + + //---- sliderPaintLabelsCheckBox ---- + sliderPaintLabelsCheckBox.setText("labels"); + sliderPaintLabelsCheckBox.addActionListener(e -> sliderPaintLabelsChanged()); + panel6.add(sliderPaintLabelsCheckBox, "cell 0 0"); + + //---- sliderInvertedCheckBox ---- + sliderInvertedCheckBox.setText("inverted"); + sliderInvertedCheckBox.addActionListener(e -> sliderInvertedChanged()); + panel6.add(sliderInvertedCheckBox, "cell 0 1"); + + //---- sliderSnapToTicksCheckBox ---- + sliderSnapToTicksCheckBox.setText("snap to ticks"); + sliderSnapToTicksCheckBox.addActionListener(e -> sliderSnapToTicksChanged()); + panel6.add(sliderSnapToTicksCheckBox, "cell 0 1"); + + //---- majorTickSpacingSpinner ---- + majorTickSpacingSpinner.setModel(new SpinnerNumberModel(50, 0, 100, 5)); + majorTickSpacingSpinner.addChangeListener(e -> majorThickSpacingChanged()); + panel6.add(majorTickSpacingSpinner, "cell 0 2"); + + //---- minorTickSpacingSpinner ---- + minorTickSpacingSpinner.setModel(new SpinnerNumberModel(10, 0, 100, 5)); + minorTickSpacingSpinner.addChangeListener(e -> minorThickSpacingChanged()); + panel6.add(minorTickSpacingSpinner, "cell 0 2"); + } + add(panel6, "cell 4 19 1 2,grow"); + //---- sliderLabel2 ---- sliderLabel2.setText("baseline"); add(sliderLabel2, "cell 0 20,alignx right,growx 0"); @@ -1262,15 +1387,34 @@ public class FlatComponentsTest progressBar2.setValue(60); add(progressBar2, "cell 1 21 3 1,growx"); - //---- indeterminateCheckBox ---- - indeterminateCheckBox.setText("indeterminate"); - indeterminateCheckBox.addActionListener(e -> indeterminateProgress()); - add(indeterminateCheckBox, "cell 4 21"); + //======== panel7 ======== + { + panel7.setBorder(new TitledBorder("JProgressBar Control")); + panel7.setLayout(new MigLayout( + "ltr,insets 0,hidemode 3", + // columns + "[]" + + "[fill]", + // rows + "[]0" + + "[]")); - //---- squareCheckBox ---- - squareCheckBox.setText("square"); - squareCheckBox.addActionListener(e -> squareChanged()); - add(squareCheckBox, "cell 4 21"); + //---- indeterminateCheckBox ---- + indeterminateCheckBox.setText("indeterminate"); + indeterminateCheckBox.addActionListener(e -> indeterminateProgress()); + panel7.add(indeterminateCheckBox, "cell 0 0"); + + //---- squareCheckBox ---- + squareCheckBox.setText("square"); + squareCheckBox.addActionListener(e -> squareChanged()); + panel7.add(squareCheckBox, "cell 1 0"); + + //---- largeHeightCheckBox ---- + largeHeightCheckBox.setText("large height"); + largeHeightCheckBox.addActionListener(e -> largeHeightChanged()); + panel7.add(largeHeightCheckBox, "cell 0 1,aligny top,growy 0"); + } + add(panel7, "cell 4 21 1 2,grow"); //---- toolTipLabel ---- toolTipLabel.setText("JToolTip:"); @@ -1284,11 +1428,6 @@ public class FlatComponentsTest toolTip2.setTipText("Tool tip with\nmultiple\nlines."); add(toolTip2, "cell 1 22 3 1"); - //---- largeHeightCheckBox ---- - largeHeightCheckBox.setText("large height"); - largeHeightCheckBox.addActionListener(e -> largeHeightChanged()); - add(largeHeightCheckBox, "cell 4 22,aligny top,growy 0"); - //---- toolBarLabel ---- toolBarLabel.setText("JToolBar:"); add(toolBarLabel, "cell 0 23"); @@ -1417,6 +1556,8 @@ public class FlatComponentsTest // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables private JTextField textField1; + private JSlider slider2; + private JSlider slider4; private FlatProgressBar progressBar3; private FlatProgressBar progressBar4; private FlatTestEnumComboBox buttonTypeComboBox; @@ -1429,7 +1570,17 @@ public class FlatComponentsTest private JRadioButton magentaOutlineRadioButton; private JRadioButton magentaCyanOutlineRadioButton; private JCheckBox focusPaintedCheckBox; + private JSlider slider1; + private JSlider slider6; + private JCheckBox sliderPaintTrackCheckBox; + private FlatTriStateCheckBox sliderPaintTicksCheckBox; + private FlatTriStateCheckBox sliderPaintLabelsCheckBox; + private JCheckBox sliderInvertedCheckBox; + private JCheckBox sliderSnapToTicksCheckBox; + private JSpinner majorTickSpacingSpinner; + private JSpinner minorTickSpacingSpinner; private JSlider slider3; + private JSlider slider5; private FlatProgressBar progressBar1; private FlatProgressBar progressBar2; private JCheckBox indeterminateCheckBox; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd index a7981e66..c67cf6ba 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.3.0.337" Java: "15" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -874,6 +874,9 @@ new FormModel { name: "slider2" "orientation": 1 "value": 30 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 13 1 6,growy" } ) @@ -885,6 +888,9 @@ new FormModel { "paintLabels": true "orientation": 1 "value": 30 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 13 1 6,growy" } ) @@ -1134,6 +1140,9 @@ new FormModel { add( new FormComponent( "javax.swing.JSlider" ) { name: "slider1" "value": 30 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 19 3 1,growx" } ) @@ -1141,9 +1150,93 @@ new FormModel { name: "slider6" "enabled": false "value": 30 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 19 3 1" } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$columnConstraints": "[]" + "$rowConstraints": "[]0[]0[]" + "$layoutConstraints": "ltr,insets 0,hidemode 3" + } ) { + name: "panel6" + "border": new javax.swing.border.TitledBorder( "JSlider Control" ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "sliderPaintTrackCheckBox" + "text": "track" + "selected": true + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "sliderPaintTrackChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTriStateCheckBox" ) { + name: "sliderPaintTicksCheckBox" + "text": "ticks" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "sliderPaintTicksChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTriStateCheckBox" ) { + name: "sliderPaintLabelsCheckBox" + "text": "labels" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "sliderPaintLabelsChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "sliderInvertedCheckBox" + "text": "inverted" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "sliderInvertedChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "sliderSnapToTicksCheckBox" + "text": "snap to ticks" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "sliderSnapToTicksChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "javax.swing.JSpinner" ) { + name: "majorTickSpacingSpinner" + "model": new javax.swing.SpinnerNumberModel( 50, 0, 100, 5 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "majorThickSpacingChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormComponent( "javax.swing.JSpinner" ) { + name: "minorTickSpacingSpinner" + "model": new javax.swing.SpinnerNumberModel( 10, 0, 100, 5 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "minorThickSpacingChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 4 19 1 2,grow" + } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "sliderLabel2" "text": "baseline" @@ -1172,6 +1265,9 @@ new FormModel { "paintLabels": true "enabled": false "value": 30 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 20 3 1" } ) @@ -1200,25 +1296,45 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 21 3 1,growx" } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "indeterminateCheckBox" - "text": "indeterminate" - auxiliary() { - "JavaCodeGenerator.variableLocal": false - } - addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "indeterminateProgress", false ) ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$columnConstraints": "[][fill]" + "$rowConstraints": "[]0[]" + "$layoutConstraints": "ltr,insets 0,hidemode 3" + } ) { + name: "panel7" + "border": new javax.swing.border.TitledBorder( "JProgressBar Control" ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "indeterminateCheckBox" + "text": "indeterminate" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "indeterminateProgress", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "squareCheckBox" + "text": "square" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "squareChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "largeHeightCheckBox" + "text": "large height" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "largeHeightChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,aligny top,growy 0" + } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 21" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "squareCheckBox" - "text": "square" - auxiliary() { - "JavaCodeGenerator.variableLocal": false - } - addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "squareChanged", false ) ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 21" + "value": "cell 4 21 1 2,grow" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "toolTipLabel" @@ -1238,16 +1354,6 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 22 3 1" } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "largeHeightCheckBox" - "text": "large height" - auxiliary() { - "JavaCodeGenerator.variableLocal": false - } - addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "largeHeightChanged", false ) ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 22,aligny top,growy 0" - } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "toolBarLabel" "text": "JToolBar:"