Google Tag Manager

Showing posts with label JSlider. Show all posts
Showing posts with label JSlider. Show all posts

2022/12/31

Inverse the display position of JSlider's ticks and knobs

Code

class VerticalFlipLayerUI extends LayerUI<JComponent> {
  @Override public void paint(Graphics g, JComponent c) {
    if (c instanceof JLayer) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setTransform(getAffineTransform(c.getSize()));
      super.paint(g2, c);
      g2.dispose();
    } else {
      super.paint(g, c);
    }
  }

  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      JLayer<?> l = (JLayer<?>) c;
      l.setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override public void eventDispatched(
        AWTEvent e, JLayer<? extends JComponent> l) {
    if (e instanceof MouseEvent) {
      MouseEvent me = (MouseEvent) e;
      Point2D pt = me.getPoint();
      try {
        pt = getAffineTransform(l.getSize()).inverseTransform(pt, null);
      } catch (NoninvertibleTransformException ex) {
        ex.printStackTrace();
        UIManager.getLookAndFeel().provideErrorFeedback(me.getComponent());
      }
      // Horizontal: me.translatePoint((int) pt.getX() - me.getX(), 0);
      me.translatePoint(0, (int) pt.getY() - me.getY());
      me.getComponent().repaint();
    }
    super.eventDispatched(e, l);
  }

  private AffineTransform getAffineTransform(Dimension d) {
    AffineTransform at = AffineTransform.getTranslateInstance(0d, d.height);
    at.scale(1d, -1d);
    return at;
  }
}

References

2022/03/30

Paint the major tick marks inside the JSlider track and the current value inside the knob

Code

JSlider slider = new JSlider();
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(10);
slider.addMouseMotionListener(new MouseAdapter() {
  @Override public void mouseDragged(MouseEvent e) {
    super.mouseDragged(e);
    e.getComponent().repaint();
  }
});

UIDefaults d = new UIDefaults();
d.put("Slider.thumbWidth", 24);
d.put("Slider.thumbHeight", 24);
Painter<JSlider> thumbPainter = (g, c, w, h) -> {
  g.setPaint(new Color(0x21_98_F6));
  g.fillOval(0, 0, w, h);
  NumberIcon icon = new NumberIcon(c.getValue());
  int xx = (w - icon.getIconWidth()) / 2;
  int yy = (h - icon.getIconHeight()) / 2;
  icon.paintIcon(c, g, xx, yy);
};
d.put("Slider:SliderThumb[Disabled].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Enabled].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused+MouseOver].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused+Pressed].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[MouseOver].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Pressed].backgroundPainter", thumbPainter);

d.put("Slider:SliderTrack[Enabled].backgroundPainter", new Painter<JSlider>() {
  @Override public void paint(Graphics2D g, JSlider c, int w, int h) {
    int arc = 10;
    int thumbSize = 24;
    int trackHeight = 8;
    int tickSize = 4;
    int trackWidth = w - thumbSize;
    int fillTop = (thumbSize - trackHeight) / 2;
    int fillLeft = thumbSize / 2;

    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.setColor(new Color(0xC6_E4_FC));
    g.fillRoundRect(fillLeft, fillTop + 2, trackWidth, trackHeight - 4, arc, arc);

    // Paint track
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.setColor(new Color(0xC6_E4_FC));
    g.fillRoundRect(fillLeft, fillTop + 2, trackWidth, trackHeight - 4, arc, arc);

    int fillBottom = fillTop + trackHeight;
    Rectangle r = new Rectangle(fillLeft, fillTop, trackWidth, fillBottom - fillTop);

    // Paint the major tick marks on the track
    g.setColor(new Color(0x31_A8_F8));
    int value = c.getMinimum();
    while (value <= c.getMaximum()) {
      int xpt = getXPositionForValue(c, r, value);
      g.fillOval(xpt, (int) r.getCenterY() - tickSize / 2, tickSize, tickSize);
      // Overflow checking
      if (Integer.MAX_VALUE - c.getMajorTickSpacing() < value) {
        break;
      }
      value += c.getMajorTickSpacing();
    }

    // JSlider.isFilled
    int fillRight = getXPositionForValue(c, r, c.getValue());
    g.setColor(new Color(0x21_98_F6));
    g.fillRoundRect(fillLeft, fillTop, fillRight - fillLeft, fillBottom - fillTop, arc, arc);
  }

  // @see javax/swing/plaf/basic/BasicSliderUI#xPositionForValue(int value)
  private int getXPositionForValue(JSlider slider, Rectangle trackRect, float value) {
    float min = slider.getMinimum();
    float max = slider.getMaximum();
    float pixelsPerValue = trackRect.width / (max - min);
    int trackLeft = trackRect.x;
    int trackRight = trackRect.x + trackRect.width - 1;
    int pos = trackLeft + Math.round(pixelsPerValue * (value - min));
    return Math.max(trackLeft, Math.min(trackRight, pos));
  }
});
slider.putClientProperty("Nimbus.Overrides", d);

References

2021/11/30

Add a vertical JSlider in the JPopupMenu and display it at the top of the JToggleButton

Code

JPopupMenu popup = new JPopupMenu();
popup.setLayout(new BorderLayout());
popup.addMouseWheelListener(InputEvent::consume);

UIManager.put("Slider.paintValue", Boolean.TRUE);
UIManager.put("Slider.focus", UIManager.get("Slider.background"));
JSlider slider = new JSlider(SwingConstants.VERTICAL, 0, 100, 80);
slider.addMouseWheelListener(e -> {
  JSlider s = (JSlider) e.getComponent();
  if (s.isEnabled()) {
    BoundedRangeModel m = s.getModel();
    m.setValue(m.getValue() - e.getWheelRotation() * 2);
  }
  e.consume();
});
popup.add(slider);

JToggleButton button = new JToggleButton("🔊") {
  @Override public JToolTip createToolTip() {
    JToolTip tip = super.createToolTip();
    tip.addHierarchyListener(e -> {
      long flg = e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED;
      if (flg != 0 && e.getComponent().isShowing()) {
        Dimension d = popup.getPreferredSize();
        popup.show(this, (getWidth() - d.width) / 2, -d.height);
      }
    });
    return tip;
  }

  @Override public Point getToolTipLocation(MouseEvent e) {
    return new Point(getWidth() / 2, -getHeight());
  }

  @Override public void setEnabled(boolean b) {
    super.setEnabled(b);
    setText(b ? "🔊" : "🔇");
  }
};
button.setToolTipText("");
button.addMouseListener(new MouseAdapter() {
  @Override public void mousePressed(MouseEvent e) {
    if (!button.isEnabled()) {
      slider.setValue(80);
      button.setEnabled(true);
    }
    Component b = (Component) e.getSource();
    Dimension d = popup.getPreferredSize();
    popup.show(b, (b.getWidth() - d.width) / 2, -d.height);
  }

  @Override public void mouseEntered(MouseEvent e) {
    if (!popup.isVisible()) {
      ToolTipManager.sharedInstance().setEnabled(true);
    }
  }

  @Override public void mouseExited(MouseEvent e) {
    if (!popup.isVisible()) {
      ToolTipManager.sharedInstance().setEnabled(true);
    }
  }
});
popup.addPopupMenuListener(new PopupMenuListener() {
  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    /* not needed */
  }

  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> ToolTipManager.sharedInstance().setEnabled(false));
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    button.setSelected(false);
  }
});

References

2020/11/30

Create switchable buttons in JSlider

Use JSlider to create switch buttons that can be toggled on and off by clicking the mouse or dragging the knob

Code

UIDefaults d = new UIDefaults();
d.put("Slider.thumbHeight", 40);
d.put("Slider.thumbWidth", 40);
d.put("Slider:SliderTrack[Enabled].backgroundPainter", (Painter) (g, c, w, h) -> {
  int arc = 40;
  int fillLeft = 2;
  int fillTop = 2;
  int trackWidth = w - fillLeft - fillLeft;
  int trackHeight = h - fillTop - fillTop;
  int baseline = trackHeight - fillTop - fillTop; // c.getBaseline(w, h);
  String off = "Off";

  g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  g.setColor(Color.GRAY);
  g.fillRoundRect(fillLeft, fillTop, trackWidth, trackHeight, arc, arc);
  g.setPaint(Color.WHITE);
  g.drawString(off, w - g.getFontMetrics().stringWidth(off) - fillLeft * 5, baseline);

  int fillRight = getXPositionForValue(c, new Rectangle(fillLeft, fillTop, trackWidth, trackHeight));
  g.setColor(Color.ORANGE);
  g.fillRoundRect(fillLeft + 1, fillTop, fillRight - fillLeft, trackHeight, arc, arc);

  g.setPaint(Color.WHITE);
  if (fillRight - fillLeft > 0) {
    g.drawString("On", fillLeft * 5, baseline);
  }
  g.setStroke(new BasicStroke(2.5f));
  g.drawRoundRect(fillLeft, fillTop, trackWidth, trackHeight, arc, arc);
});

Painter thumbPainter = (g, c, w, h) -> {
  int fillLeft = 8;
  int fillTop = 8;
  int trackWidth = w - fillLeft - fillLeft;
  int trackHeight = h - fillTop - fillTop;
  g.setPaint(Color.WHITE);
  g.fillOval(fillLeft, fillTop, trackWidth, trackHeight);
};
d.put("Slider:SliderThumb[Disabled].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Enabled].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused+MouseOver].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused+Pressed].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Focused].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[MouseOver].backgroundPainter", thumbPainter);
d.put("Slider:SliderThumb[Pressed].backgroundPainter", thumbPainter);

JSlider slider = new JSlider(0, 1, 0) {
  @Override public Dimension getPreferredSize() {
    return new Dimension(100, 40);
  }
};
slider.setFont(slider.getFont().deriveFont(Font.BOLD, 32f));
slider.putClientProperty("Nimbus.Overrides", d);

Explanation

  • Default
    • Set JSlider to Min 0, Max 1
    • Change component size by overriding getPreferredSize()
  • Thumb size
    • Resize knobs by setting Slider.thumbWidth, Slider.thumbHeight
  • SliderTrack
    • Create a Painter to draw the track background and change the background color, border, and On/Off text
    • Pressing a track turns On/Off
    • Change the drawing of the knob by setting Painter
    • ​Add MouseMotionListener to redraw the whole thing while dragging, since some afterimages may appear when you drag the knob
    • ​Pressing or clicking the knob does not cause the `On/Off 'toggle
  • JSlider + JLayer
    • ​The drawing of the track and knob is identical to the above SliderTrack
    • ​Use JLayer to set JSlider to switch On/Off regardless of track or knob
    • ​Redraw while dragging the knob is also done in LayerUI#processMouseMotionEvent(...)
class ToggleSwitchLayerUI extends LayerUI {
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override protected void processMouseEvent(MouseEvent e, JLayer l) {
    if (e.getID() == MouseEvent.MOUSE_PRESSED && SwingUtilities.isLeftMouseButton(e)) {
      e.getComponent().dispatchEvent(new MouseEvent(
          e.getComponent(),
          e.getID(), e.getWhen(),
          InputEvent.BUTTON3_DOWN_MASK, // e.getModifiers(),
          e.getX(), e.getY(),
          e.getXOnScreen(), e.getYOnScreen(),
          e.getClickCount(),
          e.isPopupTrigger(),
          MouseEvent.BUTTON3)); // e.getButton());
      e.consume();
    } else if (e.getID() == MouseEvent.MOUSE_CLICKED && SwingUtilities.isLeftMouseButton(e)) {
      JSlider slider = l.getView();
      int v = slider.getValue();
      if (slider.getMinimum() == v) {
        slider.setValue(slider.getMaximum());
      } else if (slider.getMaximum() == v) {
        slider.setValue(slider.getMinimum());
      }
    }
  }

  @Override protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
    l.getView().repaint();
  }
}

References

2019/02/27

Limit the movable range of JSlider's value

Code

public static final int MAXI = 80;
public static final int MINI = 40;

private static class MetalDragLimitedSliderUI extends MetalSliderUI {
  @Override protected TrackListener createTrackListener(JSlider slider) {
    return new TrackListener() {
      @Override public void mouseDragged(MouseEvent e) {
        // case HORIZONTAL:
        int halfThumbWidth = thumbRect.width / 2;
        int thumbLeft = e.getX() - offset;
        int maxPos = xPositionForValue(MAXI) - halfThumbWidth;
        int minPos = xPositionForValue(MINI) - halfThumbWidth;
        if (thumbLeft > maxPos) {
          e.translatePoint(maxPos + offset - e.getX(), 0);
        } else if (thumbLeft < minPos) {
          e.translatePoint(minPos + offset - e.getX(), 0);
        }
        super.mouseDragged(e);
      }
    };
  }
}

private static JSlider makeSilder(String title) {
  JSlider slider = new JSlider(0, 100, 40);
  slider.setBorder(BorderFactory.createTitledBorder(title));
  slider.setMajorTickSpacing(10);
  slider.setPaintTicks(true);
  slider.setPaintLabels(true);
  Dictionary<?, ?> dictionary = slider.getLabelTable();
  if (Objects.nonNull(dictionary)) {
    Enumeration<?> elements = dictionary.elements();
    while (elements.hasMoreElements()) {
      JLabel label = (JLabel) elements.nextElement();
      int v = Integer.parseInt(label.getText());
      if (v > MAXI || v < MINI) {
        label.setForeground(Color.RED);
      }
    }
  }
  slider.getModel().addChangeListener(e -> {
    BoundedRangeModel m = (BoundedRangeModel) e.getSource();
    if (m.getValue() > MAXI) {
      m.setValue(MAXI);
    } else if (m.getValue() < MINI) {
      m.setValue(MINI);
    }
  });
  return slider;
}

References

2016/06/27

Change the color of the label represents the current value of a JSlider

Code

slider.getModel().addChangeListener(new ChangeListener() {
  private int prev = -1;
  private void resetForeground(Object o, Color c) {
    if (o instanceof Component) {
      ((Component) o).setForeground(c);
    }
  }

  @Override public void stateChanged(ChangeEvent e) {
    BoundedRangeModel m = (BoundedRangeModel) e.getSource();
    int i = m.getValue();
    if ((slider.getMajorTickSpacing() == 0 ||
         i % slider.getMajorTickSpacing() == 0) && i != prev) {
      Dictionary dictionary = slider.getLabelTable();
      resetForeground(dictionary.get(i), Color.RED);
      resetForeground(dictionary.get(prev), Color.BLACK);
      slider.repaint();
      prev = i;
    }
  }
});

References

2015/10/30

Display tooltip while dragging JSlider thumb

Code

class SliderPopupListener extends MouseAdapter {
  private final JWindow toolTip = new JWindow();
  private final JLabel label = new JLabel("", SwingConstants.CENTER);
  private final Dimension size = new Dimension(30, 20);
  private int prevValue = -1;

  public SliderPopupListener() {
    label.setOpaque(false);
    label.setBackground(UIManager.getColor("ToolTip.background"));
    label.setBorder(UIManager.getBorder("ToolTip.border"));
    toolTip.add(label);
    toolTip.setSize(size);
  }

  protected void updateToolTip(MouseEvent me) {
    JSlider slider = (JSlider) me.getSource();
    int intValue = (int) slider.getValue();
    if (prevValue != intValue) {
      label.setText(String.format("%03d", slider.getValue()));
      Point pt = me.getPoint();
      pt.y = -size.height;
      SwingUtilities.convertPointToScreen(pt, (Component) me.getSource());
      pt.translate(-size.width / 2, 0);
      toolTip.setLocation(pt);
    }
    prevValue = intValue;
  }

  @Override public void mouseDragged(MouseEvent me) {
    updateToolTip(me);
  }

  @Override public void mousePressed(MouseEvent me) {
    toolTip.setVisible(true);
    updateToolTip(me);
  }

  @Override public void mouseReleased(MouseEvent me) {
    toolTip.setVisible(false);
  }
}

References

2014/06/02

How to create a circular progress component

Code

class ProgressCircleUI extends BasicProgressBarUI {
  @Override public Dimension getPreferredSize(JComponent c) {
    Dimension d = super.getPreferredSize(c);
    int v = Math.max(d.width, d.height);
    d.setSize(v, v);
    return d;
  }

  @Override public void paint(Graphics g, JComponent c) {
    Insets b = progressBar.getInsets(); // area for border
    int barRectWidth  = progressBar.getWidth()  - b.right - b.left;
    int barRectHeight = progressBar.getHeight() - b.top - b.bottom;
    if (barRectWidth <= 0 || barRectHeight <= 0) {
      return;
    }

    // draw the cells
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    double degree = 360 * progressBar.getPercentComplete();
    double sz = Math.min(barRectWidth, barRectHeight);
    double cx = b.left + barRectWidth  * .5;
    double cy = b.top  + barRectHeight * .5;
    double or = sz * .5;
    double ir = or * .5; //or - 20;
    Shape inner  = new Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2);
    Shape outer  = new Ellipse2D.Double(cx - or, cy - or, sz, sz);
    Shape sector = new Arc2D.Double(
        cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE);

    Area foreground = new Area(sector);
    Area background = new Area(outer);
    Area hole = new Area(inner);

    foreground.subtract(hole);
    background.subtract(hole);

    g2.setPaint(new Color(0xDDDDDD));
    g2.fill(background);

    g2.setPaint(progressBar.getForeground());
    g2.fill(foreground);
    g2.dispose();

    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
      paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b);
    }
  }
}

// ...
JProgressBar progress = new JProgressBar();
progress.setUI(new ProgressCircleUI());
progress.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
progress.setStringPainted(true);
progress.setFont(progress.getFont().deriveFont(24f));
progress.setForeground(Color.ORANGE);

(new Timer(50, e -> {
  int iv = Math.min(100, progress.getValue() + 1);
  progress.setValue(iv);
})).start();

References

2011/06/27

Gradient translucent track JSlider

Code

UIManager.put("Slider.horizontalThumbIcon", new Icon() {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {}
  @Override public int getIconWidth()  { return 15; }
  @Override public int getIconHeight() { return 64; }
});
UIManager.put("Slider.trackWidth", 64);
UIManager.put("Slider.majorTickLength", 6);

JSlider slider = makeSlider();
slider.setUI(new MetalSliderUI() {
  int[] pallet = makeGradientPallet();
  @Override public void paintTrack(Graphics g) {
    // Color trackColor = !slider.isEnabled()
    //   ? MetalLookAndFeel.getControlShadow() : slider.getForeground();
    boolean leftToRight = true; //MetalUtils.isLeftToRight(slider);
    Color controlDarkShadow = Color.GRAY;
    Color controlHighlight = new Color(200, 255, 200);
    Color controlShadow = new Color(0, 100, 0);

    g.translate(trackRect.x, trackRect.y);

    int trackLeft = 0;
    int trackTop = 0;
    int trackRight = 0;
    int trackBottom = 0;

    // Draw the track
    if (slider.getOrientation() == SwingConstants.HORIZONTAL) {
      trackBottom = (trackRect.height - 1) - getThumbOverhang();
      trackTop = trackBottom - (getTrackWidth() - 1);
      trackRight = trackRect.width - 1;
    } else {
      if (leftToRight) {
        trackLeft = (trackRect.width - getThumbOverhang()) - getTrackWidth();
        trackRight = (trackRect.width - getThumbOverhang()) - 1;
      } else {
        trackLeft = getThumbOverhang();
        trackRight = getThumbOverhang() + getTrackWidth() - 1;
      }
      trackBottom = trackRect.height - 1;
    }

    if (slider.isEnabled()) {
      g.setColor(controlDarkShadow);
      g.drawRect(trackLeft, trackTop,
                (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1);

      g.setColor(controlHighlight);
      g.drawLine(trackLeft + 1, trackBottom, trackRight, trackBottom);
      g.drawLine(trackRight, trackTop + 1, trackRight, trackBottom);

      g.setColor(controlShadow);
      g.drawLine(trackLeft + 1, trackTop + 1, trackRight - 2, trackTop + 1);
      g.drawLine(trackLeft + 1, trackTop + 1, trackLeft + 1, trackBottom - 2);
    } else {
      g.setColor(controlShadow);
      g.drawRect(trackLeft, trackTop,
                 (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1);
    }

    // Draw the fill
    int middleOfThumb = 0;
    int fillTop = 0;
    int fillLeft = 0;
    int fillBottom = 0;
    int fillRight = 0;

    if (slider.getOrientation() == SwingConstants.HORIZONTAL) {
      middleOfThumb = thumbRect.x + (thumbRect.width / 2);
      middleOfThumb -= trackRect.x; // To compensate for the g.translate()
      fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
      fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;

      if (!drawInverted()) {
        fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
        fillRight = middleOfThumb;
      } else {
        fillLeft = middleOfThumb;
        fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
      }
    } else {
      middleOfThumb = thumbRect.y + (thumbRect.height / 2);
      middleOfThumb -= trackRect.y; // To compensate for the g.translate()
      fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
      fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;

      if (!drawInverted()) {
        fillTop = middleOfThumb;
        fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
      } else {
        fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
        fillBottom = middleOfThumb;
      }
    }

    if (slider.isEnabled()) {
      g.setColor(slider.getBackground());
      g.drawLine(fillLeft, fillTop, fillRight, fillTop );
      g.drawLine(fillLeft, fillTop, fillLeft, fillBottom );

      double x = (fillRight - fillLeft) / (double) (trackRight - trackLeft);
      g.setColor(getColorFromPallet(pallet, x));
      g.fillRect(fillLeft + 1, fillTop + 1,
                 fillRight - fillLeft, fillBottom - fillTop);
    } else {
      g.setColor(controlShadow);
      g.fillRect(fillLeft, fillTop, fillRight - fillLeft, trackBottom - trackTop);
    }

    int yy = trackTop + (trackBottom - trackTop) / 2;
    for (int i = 10; i >= 0; i--) {
      g.setColor(new Color(1f, 1f, 1f, i * .07f));
      g.drawLine(trackLeft + 2, yy, (trackRight - trackLeft), yy);
      yy--;
    }
    g.translate(-trackRect.x, -trackRect.y);
  }
});

// ...
private static int[] makeGradientPallet() {
  BufferedImage image = new BufferedImage(100, 1, BufferedImage.TYPE_INT_RGB);
  Graphics2D g2  = image.createGraphics();
  Point2D start  = new Point2D.Float(0f, 0f);
  Point2D end    = new Point2D.Float(99f, 0f);
  float[] dist   = {0f, .5f, 1f};
  Color[] colors = {Color.RED, Color.YELLOW, Color.GREEN};
  g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
  g2.fillRect(0, 0, 100, 1);
  int width  = image.getWidth(null);
  int[] pallet = new int[width];
  PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
  try {
    pg.grabPixels();
  } catch (Exception e) {
    e.printStackTrace();
  }
  return pallet;
}

private static Color getColorFromPallet(int[] pallet, float x) {
  int i = (int) (pallet.length * x);
  int max = pallet.length-1;
  int index = i < 0 ? 0 : i > max ? max : i;
  int pix = pallet[index] & 0x00_FF_FF_FF | (0x64 << 24);
  return new Color(pix, true);
}

References

2009/12/21

snap to ticks drag JSlider

Code

slider.setUI(new MetalSliderUI() {
  @Override protected TrackListener createTrackListener(final JSlider slider) {
    return new TrackListener() {
      @Override public void mouseDragged(MouseEvent e) {
        if (!slider.getSnapToTicks() || slider.getMajorTickSpacing() == 0) {
          super.mouseDragged(e);
          return;
        }
        // case SwingConstants.HORIZONTAL:
        int halfThumbWidth = thumbRect.width / 2;
        final int trackLength = trackRect.width;
        final int trackLeft = trackRect.x - halfThumbWidth;
        final int trackRight = trackRect.x + trackRect.width - 1 + halfThumbWidth;
        int xPos = e.getX();
        int snappedPos = xPos;
        if (xPos <= trackLeft) {
          snappedPos = trackLeft;
        } else if (xPos >= trackRight) {
          snappedPos = trackRight;
        } else {
          // int tickSpacing = slider.getMajorTickSpacing();
          // float actualPixelsForOneTick =
          //     trackLength * tickSpacing / (float) slider.getMaximum();

          // a problem if you choose to set a negative MINIMUM for the JSlider;
          // the calculated drag-positions are wrong.
          // Fixed by bobndrew:
          int possibleTickPositions = slider.getMaximum() - slider.getMinimum();
          int tickSpacing = (slider.getMinorTickSpacing() == 0)
              ? slider.getMajorTickSpacing()
              : slider.getMinorTickSpacing();
          float actualPixelsForOneTick =
              trackLength * tickSpacing / (float) possibleTickPositions;
          xPos -= trackLeft;
          snappedPos = (int) (Math.round(
              xPos / actualPixelsForOneTick) * actualPixelsForOneTick + .5) + trackLeft;
          offset = 0;
          // System.out.println(snappedPos);
        }
        e.translatePoint(snappedPos - e.getX(), 0);
        super.mouseDragged(e);
      }
    };
  }
});

References

2009/11/30

Jump to clicked position JSlider

Code

slider.setUI(new MetalSliderUI() {
  @Override protected TrackListener createTrackListener(JSlider slider) {
    return new TrackListener() {
      @Override public void mousePressed(MouseEvent e) {
        if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag")
              && SwingUtilities.isLeftMouseButton(e)) {
          JSlider slider = (JSlider) e.getComponent();
          switch (slider.getOrientation()) {
          case SwingConstants.VERTICAL:
            slider.setValue(valueForYPosition(e.getY()));
            break;
          case SwingConstants.HORIZONTAL:
            slider.setValue(valueForXPosition(e.getX()));
            break;
          default:
            throw new IllegalArgumentException(
                "orientation must be one of: VERTICAL, HORIZONTAL");
          }
          super.mousePressed(e); //isDragging = true;
          super.mouseDragged(e);
        } else {
          super.mousePressed(e);
        }
      }

      @Override public boolean shouldScroll(int direction) {
        return false;
      }
    };
  }
});

References

2009/02/26

Use JScrollBar as JSlider

Code

int step = 5;
int extent = 20;
int min = 0;
int max = extent * 10; // 200
int value = 50;
JScrollBar scrollbar = new JScrollBar(
  JScrollBar.HORIZONTAL, value, extent, min, max + extent);
scrollbar.setUnitIncrement(step);
scrollbar.getModel().addChangeListener(new ChangeListener() {
  @Override public void stateChanged(ChangeEvent e) {
    BoundedRangeModel m = (BoundedRangeModel) e.getSource();
    spinner.setValue(m.getValue());
  }
});
JSpinner spinner = new JSpinner(new SpinnerNumberModel(value, min, max, step));

References