Google Tag Manager

2019/12/30

Create a speedometer by changing the shape of JProgressBar to a donut-shaped semicircle

Code

class SolidGaugeUI extends BasicProgressBarUI {
  private final int[] pallet;
  private final double extent;

  protected SolidGaugeUI(int range, double extent) {
    super();
    this.pallet = makeGradientPallet(range);
    this.extent = extent;
  }

  @Override public void paint(Graphics g, JComponent c) {
    Rectangle rect = SwingUtilities.calculateInnerArea(progressBar, null);
    if (rect.isEmpty()) {
      return;
    }
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);

    // double extent = -150d;
    double start = 90d + extent * .5;
    double degree = extent * progressBar.getPercentComplete();
    double or = Math.min(rect.width, rect.height);
    double cx = rect.getCenterX();
    double cy = rect.getMaxY();
    double sz = or * 2d;
    double ir = or * .6;
    Shape inner = new Arc2D.Double(
        cx - ir, cy - ir, ir * 2d, ir * 2d, start, -extent, Arc2D.PIE);
    Shape outer = new Arc2D.Double(
        cx - or, cy - or, sz, sz, start, -extent, Arc2D.PIE);
    Shape sector = new Arc2D.Double(
        cx - or, cy - or, sz, sz, start, -degree, Arc2D.PIE);

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

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

    // Draw the track
    g2.setPaint(new Color(0xDD_DD_DD));
    g2.fill(background);

    // Draw the circular sector
    g2.setPaint(getColorFromPallet(
        pallet, progressBar.getPercentComplete()));
    g2.fill(foreground);

    // Draw ...
    Font font = progressBar.getFont();
    float fsz = font.getSize2D();
    float min = (float) (cx - or - fsz);
    float max = (float) (cx + or + 4d);
    g2.setPaint(progressBar.getForeground());
    g2.drawString(
        Objects.toString(progressBar.getMinimum()), min, (float) cy);
    g2.drawString(Objects.toString(
        progressBar.getMaximum()), max, (float) cy);

    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
      float h = (float) cy - fsz;
      String str = String.format("%d", progressBar.getValue());
      float vx = (float) cx - g2.getFontMetrics().stringWidth(str) * .5f;
      g2.drawString(str, vx, h);
      float ksz = fsz * .66f;
      g2.setFont(font.deriveFont(ksz));
      String kmh = "㎞/h";
      float tx = (float) cx - g2.getFontMetrics().stringWidth(kmh) * .5f;
      g2.drawString(kmh, tx, h + ksz);
    }
    g2.dispose();
  }

  private static int[] makeGradientPallet(int range) {
    BufferedImage image = new BufferedImage(
        range, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = image.createGraphics();
    Point2D start = new Point2D.Float();
    Point2D end = new Point2D.Float(range - 1f, 0f);
    float[] dist = {0f, .8f, .9f, 1f};
    Color[] colors = {Color.GREEN, Color.YELLOW, Color.ORANGE, Color.RED};
    g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
    g2.fillRect(0, 0, range, 1);
    g2.dispose();

    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 (InterruptedException ex) {
      ex.printStackTrace();
      Toolkit.getDefaultToolkit().beep();
      Thread.currentThread().interrupt();
    }
    return pallet;
  }

  private static Color getColorFromPallet(int[] pallet, double pos) {
    if (pos < 0d || pos > 1d) {
      throw new IllegalArgumentException("Parameter outside of expected range");
    }
    int i = (int) (pallet.length * pos);
    int max = pallet.length - 1;
    int index = Math.min(Math.max(i, 0), max);
    return new Color(pallet[index] & 0x00_FF_FF_FF);
  }
}

References

2019/11/29

Change the word selection behavior by double clicking the mouse in JTextArea

Code

class SelectWordCaret extends DefaultCaret {
  private SelectingMode selectingMode = SelectingMode.CHAR;
  private int p0; // = Math.min(getDot(), getMark());
  private int p1; // = Math.max(getDot(), getMark());

  @Override public void mousePressed(MouseEvent e) {
    super.mousePressed(e);
    int clickCount = e.getClickCount();
    if (SwingUtilities.isLeftMouseButton(e) && !e.isConsumed()) {
      if (clickCount == 2) {
        selectingMode = SelectingMode.WORD;
        p0 = Math.min(getDot(), getMark());
        p1 = Math.max(getDot(), getMark());
      } else if (clickCount >= 3) {
        selectingMode = SelectingMode.ROW;
        JTextComponent target = getComponent();
        int offs = target.getCaretPosition();
        try {
          p0 = Utilities.getRowStart(target, offs);
          p1 = Utilities.getRowEnd(target, offs);
          setDot(p0);
          moveDot(p1);
        } catch (BadLocationException ex) {
          UIManager.getLookAndFeel().provideErrorFeedback(target);
        }
      }
    } else {
      selectingMode = SelectingMode.CHAR;
    }
  }

  @Override public void mouseDragged(MouseEvent e) {
    if (!e.isConsumed() && SwingUtilities.isLeftMouseButton(e)) {
      if (selectingMode == SelectingMode.WORD) {
        continuouslySelectWords(e);
      } else if (selectingMode == SelectingMode.ROW) {
        continuouslySelectRows(e);
      }
    } else {
      super.mouseDragged(e);
    }
  }

  // If you continue to drag the mouse after selecting a word with double press:
  private void continuouslySelectWords(MouseEvent e) {
    Position.Bias[] biasRet = new Position.Bias[1];
    JTextComponent c = getComponent();
    int pos = getCaretPositionByLocation(c, e.getPoint(), biasRet);
    try {
      if (p0 < pos && pos < p1) {
        setDot(p0);
        moveDot(p1, biasRet[0]);
      } else if (p1 < pos) {
        setDot(p0);
        moveDot(Utilities.getWordEnd(c, pos), biasRet[0]);
      } else if (p0 > pos) {
        setDot(p1);
        moveDot(Utilities.getWordStart(c, pos), biasRet[0]);
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(c);
    }
  }

  // If you drag the mouse continuously after selecting the entire line in triple press:
  private void continuouslySelectRows(MouseEvent e) {
    Position.Bias[] biasRet = new Position.Bias[1];
    JTextComponent c = getComponent();
    int pos = getCaretPositionByLocation(c, e.getPoint(), biasRet);
    try {
      if (p0 < pos && pos < p1) {
        setDot(p0);
        moveDot(p1, biasRet[0]);
      } else if (p1 < pos) {
        setDot(p0);
        moveDot(Utilities.getRowEnd(c, pos), biasRet[0]);
      } else if (p0 > pos) {
        setDot(p1);
        moveDot(Utilities.getRowStart(c, pos), biasRet[0]);
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(c);
    }
  }
  // ...
}

References

2019/10/30

Wraps JPanel child components horizontally and dynamically expands their horizontal spacing

Code

class ScrollableWrapLayout extends FlowLayout {
  private final int fixedHorizontalGap;

  protected ScrollableWrapLayout(int align, int hgap, int vgap) {
    super(align, hgap, vgap);
    fixedHorizontalGap = hgap;
  }

  private int getPreferredHorizontalGap(Container target) {
    Insets insets = target.getInsets();
    int columns = 0;
    int width = target.getWidth();
    if (target.getParent() instanceof JViewport) {
      width = target.getParent().getBounds().width;
    }
    width -= insets.left + insets.right + fixedHorizontalGap * 2;
    for (int i = 0; i < target.getComponentCount(); i++) {
      Component m = target.getComponent(i);
      if (m.isVisible()) {
        Dimension d = m.getPreferredSize();
        if (width - d.width - fixedHorizontalGap < 0) {
          columns = i;
          break;
        }
        width -= d.width + fixedHorizontalGap;
      }
    }
    return fixedHorizontalGap + width / columns;
  }

  @Override public void layoutContainer(Container target) {
    setHgap(getPreferredHorizontalGap(target));
    super.layoutContainer(target);
  }

  @Override public Dimension preferredLayoutSize(Container target) {
    Dimension dim = super.preferredLayoutSize(target);
    synchronized (target.getTreeLock()) {
      if (target.getParent() instanceof JViewport) {
        dim.width = target.getParent().getBounds().width;
        for (int i = 0; i < target.getComponentCount(); i++) {
          Component m = target.getComponent(i);
          if (m.isVisible()) {
            Dimension d = m.getPreferredSize();
            dim.height = Math.max(dim.height, d.height + m.getY());
          }
        }
      }
      return dim;
    }
  }
}

References

2019/09/30

Change drawing area of selected highlight in JEditorPane

Code

class ParagraphMarkHighlightPainter extends DefaultHighlightPainter {
  protected ParagraphMarkHighlightPainter(Color color) {
    super(color);
  }

  @Override public Shape paintLayer(
      Graphics g, int offs0, int offs1,
      Shape bounds, JTextComponent c, View view) {
    Shape s = super.paintLayer(g, offs0, offs1, bounds, c, view);
    Rectangle r = s.getBounds();
    if (r.width - 1 <= 0) {
      g.fillRect(r.x + r.width, r.y, r.width + r.height / 2, r.height);
    }
    return s;
  }
}

class WholeLineHighlightPainter extends DefaultHighlightPainter {
  protected WholeLineHighlightPainter(Color color) {
    super(color);
  }

  @Override public Shape paintLayer(
      Graphics g, int offs0, int offs1,
      Shape bounds, JTextComponent c, View view) {
    Rectangle rect = bounds.getBounds();
    rect.width = c.getSize().width;
    return super.paintLayer(g, offs0, offs1, rect, c, view);
  }
}

References

2019/08/31

Create a minimap of JEditorPane with ImageIcon and overlay it on JScrollPane using LayoutManager

Code

private final JEditorPane editor = new JEditorPane();
private final JScrollPane scroll = new JScrollPane(editor);
private final JLabel label = new JLabel() {
  private transient MouseInputListener handler;
  @Override public void updateUI() {
    removeMouseListener(handler);
    removeMouseMotionListener(handler);
    super.updateUI();
    handler = new MiniMapHandler();
    addMouseListener(handler);
    addMouseMotionListener(handler);
  }

  @Override public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Container c = SwingUtilities.getAncestorOfClass(JViewport.class, editor);
    if (!(c instanceof JViewport) || editor == null) {
      return;
    }
    JViewport vport = (JViewport) c;
    Rectangle vrect = vport.getBounds(); // scroll.getViewportBorderBounds();
    Rectangle erect = editor.getBounds();
    Rectangle crect = SwingUtilities.calculateInnerArea(this, new Rectangle());

    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    double sy = crect.getHeight() / erect.getHeight();
    AffineTransform at = AffineTransform.getScaleInstance(1d, sy);

    // paint Thumb
    Rectangle thumbRect = new Rectangle(vrect);
    thumbRect.y = vport.getViewPosition().y;
    Rectangle r = at.createTransformedShape(thumbRect).getBounds();
    int y = crect.y + r.y;
    g2.setColor(THUMB_COLOR);
    g2.fillRect(0, y, crect.width, r.height);
    g2.setColor(THUMB_COLOR.darker());
    g2.drawRect(0, y, crect.width - 1, r.height - 1);
    g2.dispose();
  }
};

private class MiniMapHandler extends MouseInputAdapter {
  @Override public void mousePressed(MouseEvent e) {
    processMiniMapMouseEvent(e);
  }

  @Override public void mouseDragged(MouseEvent e) {
    processMiniMapMouseEvent(e);
  }

  protected final void processMiniMapMouseEvent(MouseEvent e) {
    Point pt = e.getPoint();
    Component c = (Component) e.getComponent();
    BoundedRangeModel m = scroll.getVerticalScrollBar().getModel();
    int brm = m.getMaximum() - m.getMinimum();
    int iv = (int) (.5 - m.getExtent() * .5 + pt.y * brm / (double) c.getHeight());
    m.setValue(iv);
  }
}
// ...
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> label.repaint());

JPanel pp = new JPanel(new BorderLayout(0, 0));
pp.add(label, BorderLayout.NORTH);
JScrollPane minimap = new JScrollPane(pp);
minimap.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
minimap.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

Box box = Box.createHorizontalBox();
box.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
box.add(Box.createHorizontalGlue());
box.add(button);

JPanel p = new JPanel() {
  @Override public boolean isOptimizedDrawingEnabled() {
    return false;
  }
};
p.setLayout(new BorderLayout(0, 0) {
  @Override public void layoutContainer(Container parent) {
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int width = parent.getWidth();
      int height = parent.getHeight();
      int top = insets.top;
      int bottom = height - insets.bottom;
      int left = insets.left;
      int right = width - insets.right;
      Component ec = getLayoutComponent(parent, BorderLayout.EAST);
      if (Objects.nonNull(ec)) {
        Dimension d = ec.getPreferredSize();
        JScrollBar vsb = scroll.getVerticalScrollBar();
        int vsw = vsb.isVisible() ? vsb.getSize().width : 0;
        ec.setBounds(right - d.width - vsw, top, d.width, bottom - top);
      }
      Component cc = getLayoutComponent(parent, BorderLayout.CENTER);
      if (Objects.nonNull(cc)) {
        cc.setBounds(left, top, right - left, bottom - top);
      }
    }
  }
});
p.add(minimap, BorderLayout.EAST);
p.add(scroll);

References

2019/07/30

Enable focus move by tab key in cell editor of JTable

Code

JTable table = makeTable();
ActionMap am = table.getActionMap();
Action sncc = am.get("selectNextColumnCell");
Action action = new AbstractAction() {
  @Override public void actionPerformed(ActionEvent e) {
    if (!table.isEditing() || !isEditorFocusCycle(table.getEditorComponent())) {
      // System.out.println("Exit editor");
      sncc.actionPerformed(e);
    }
  }
};
am.put("selectNextColumnCell2", action);

InputMap im = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "selectNextColumnCell2");
// ...
protected boolean isEditorFocusCycle(Component editor) {
  Component child = CheckBoxesEditor.getEditorFocusCycleAfter(editor);
  if (child != null) {
    child.requestFocus();
    return true;
  }
  return false;
}
// ...
public static Component getEditorFocusCycleAfter(Component editor) {
  Component fo = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
  if (fo == null || !(editor instanceof Container)) {
    return null;
  }
  Container root = (Container) editor;
  if (!root.isFocusCycleRoot()) {
    root = root.getFocusCycleRootAncestor();
  }
  if (root == null) {
    return null;
  }
  // System.out.println("FocusCycleRoot: " + root.getClass().getName());
  FocusTraversalPolicy ftp = root.getFocusTraversalPolicy();
  Component child = ftp.getComponentAfter(root, fo);
  if (child != null && SwingUtilities.isDescendingFrom(child, editor)) {
    // System.out.println("requestFocus: " + child.getClass().getName());
    // child.requestFocus();
    return child;
  }
  return null;
}

References

2019/06/28

Make JToolTip translucent, and change its shape and display position

Code

class BalloonToolTip extends JToolTip {
  private static final int TRI_HEIGHT = 4;
  private HierarchyListener listener;

  @Override public void updateUI() {
    removeHierarchyListener(listener);
    super.updateUI();
    listener = e -> {
      Component c = e.getComponent();
      if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0
          && c.isShowing()) {
        Optional.ofNullable(SwingUtilities.getRoot(c))
           .filter(JWindow.class::isInstance).map(JWindow.class::cast)
           .ifPresent(w -> w.setBackground(new Color(0x0, true)));
      }
    };
    addHierarchyListener(listener);
    setOpaque(false);
    setForeground(Color.WHITE);
    setBackground(new Color(0xC8_00_00_00, true));
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5 + TRI_HEIGHT, 5));
  }

  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 32;
    return d;
  }

  @Override protected void paintComponent(Graphics g) {
    Shape s = makeBalloonShape();
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setColor(getBackground());
    g2.fill(s);
    g2.dispose();
    super.paintComponent(g);
  }

  private Shape makeBalloonShape() {
    int w = getWidth() - 1;
    int h = getHeight() - TRI_HEIGHT - 1;
    int r = 10;
    int cx = getWidth() / 2;
    Polygon triangle = new Polygon();
    triangle.addPoint(cx - TRI_HEIGHT, h);
    triangle.addPoint(cx, h + TRI_HEIGHT);
    triangle.addPoint(cx + TRI_HEIGHT, h);
    Area area = new Area(new RoundRectangle2D.Float(0, 0, w, h, r, r));
    area.add(new Area(triangle));
    return area;
  }
}

// ...
@Override public String getToolTipText(MouseEvent e) {
  Point p = e.getPoint();
  int idx = locationToIndex(p);
  Rectangle rect = getCellBounds(idx, idx);
  if (idx < 0 || !rect.contains(p.x, p.y)) {
    return null;
  }
  Contribution value = getModel().getElementAt(idx);
  String act = value.activity == 0 ? "No" : Objects.toString(value.activity);
  return "<html>" + act + " contribution <span style='color:#C8C8C8'> on "
      + value.date.toString();
}

@Override public Point getToolTipLocation(MouseEvent e) {
  Point p = e.getPoint();
  int i = locationToIndex(p);
  Rectangle rect = getCellBounds(i, i);

  String toolTipText = getToolTipText(e);
  if (Objects.nonNull(toolTipText)) {
    JToolTip tip = createToolTip();
    tip.setTipText(toolTipText);
    Dimension d = tip.getPreferredSize();
    int gap = 2;
    return new Point((int) (rect.getCenterX() - d.getWidth() / 2d),
                     rect.y - d.height - gap);
  }
  return null;
}

@Override public JToolTip createToolTip() {
  if (tip == null) {
    tip = new BalloonToolTip();
    tip.setComponent(this);
  }
  return tip;
}

References

2019/05/28

Draw a color wheel on JPanel

Code

// Colors: a Color Dialog | Java Graphics
// https://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html
private BufferedImage updateImage() {
  BufferedImage image = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
  int[] row = new int[SIZE];
  float size = (float) SIZE;
  float radius = size / 2f;
  for (int yidx = 0; yidx < SIZE; yidx++) {
    float y = yidx - radius;
    for (int xidx = 0; xidx < SIZE; xidx++) {
      float x = xidx - radius;
      double theta = Math.atan2(y, x) - 3d * Math.PI / 2d;
      if (theta < 0) {
        theta += 2d * Math.PI;
      }
      double r = Math.hypot(x, y); // Math.sqrt(x * x + y * y);
      float hue = (float) (theta / (2d * Math.PI));
      float sat = Math.min((float) (r / radius), 1f);
      float bri = 1f;
      row[xidx] = Color.HSBtoRGB(hue, sat, bri);
    }
    image.getRaster().setDataElements(0, yidx, SIZE, 1, row);
  }
  return image;
}

// campbell: Java 2D Trickery: Soft Clipping Blog | Oracle Community
// https://community.oracle.com/blogs/campbell/2006/07/19/java-2d-trickery-soft-clipping
GraphicsConfiguration gc = g2.getDeviceConfiguration();
BufferedImage buf = gc.createCompatibleImage(s, s, Transparency.TRANSLUCENT);
Graphics2D g2d = buf.createGraphics();

g2d.setComposite(AlphaComposite.Src);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fill(new Ellipse2D.Float(0f, 0f, s, s));

g2d.setComposite(AlphaComposite.SrcAtop);
g2d.drawImage(colorWheelImage, 0, 0, null);
g2d.dispose();

g2.drawImage(buf, null, (getWidth() - s) / 2, (getHeight() - s) / 2);
g2.dispose();

References

2019/04/25

Use the GridBagLayout to layout the JButton like a keyboard

Code

private static final String[][] KEYS = {
  {"`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BS"},
  {"Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\", ""},
  {"Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter", ""},
  {"Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "", "↑"},
  {"Fn", "Alt", "                             ", "Alt", "←", "↓", "→"}
};

private static Component makeKeyboardPanel() {
  JPanel keyboard = new JPanel(new GridBagLayout());

  GridBagConstraints c = new GridBagConstraints();
  c.fill = GridBagConstraints.BOTH;
  c.gridy = 50;
  for (int i = 0; i < KEYS[0].length * 2; i++) {
    c.gridx = i;
    keyboard.add(Box.createHorizontalStrut(KeyButton.SIZE));
  }

  for (int row = 0; row < KEYS.length; row++) {
    c.gridx = 0;
    c.gridy = row;
    for (int col = 0; col < KEYS[row].length; col++) {
      String key = KEYS[row][col];
      int len = key.length();
      c.gridwidth = len > 10 ? 14
                  : len > 4  ? 4
                  : len > 1  ? 3
                  : len == 1 ? 2 : 1;
      if (key.isEmpty()) {
        keyboard.add(Box.createHorizontalStrut(KeyButton.SIZE), c);
      } else {
        keyboard.add(new KeyButton(key, len <= 2), c);
      }
      c.gridx += c.gridwidth;
    }
  }
  EventQueue.invokeLater(() -> SwingUtilities.updateComponentTreeUI(keyboard));
  return keyboard;
}

References

2019/03/27

Perform hover effect animation using RadialGradientPaint on soft clipped JButton

Code

protected void update() {
  if (!getBounds().equals(base)) {
    base = getBounds();
    int w = getWidth();
    int h = getHeight();
    if (w > 0 && h > 0) {
      buf = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    }
    shape = new RoundRectangle2D.Double(
      0, 0, w - 1, h - 1, ARC_WIDTH, ARC_HEIGHT);
  }
  if (buf == null) {
    return;
  }

  Graphics2D g2 = buf.createGraphics();
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                      RenderingHints.VALUE_ANTIALIAS_ON);
  Color c1 = new Color(0x00_F7_23_59, true);
  Color c2 = new Color(0x64_44_05_F7, true);

  g2.setComposite(AlphaComposite.Clear);
  g2.fillRect(0, 0, getWidth(), getHeight());

  g2.setComposite(AlphaComposite.Src);
  g2.setPaint(new Color(getModel().isArmed() ? 0xFF_AA_AA : 0xF7_23_59));
  g2.fill(shape);

  if (radius > 0) {
    int cx = pt.x - radius;
    int cy = pt.y - radius;
    int r2 = radius + radius;
    float[] dist = { 0f, 1f };
    Color[] colors = { c2, c1 };
    g2.setPaint(new RadialGradientPaint(pt, r2, dist, colors));
    g2.setComposite(AlphaComposite.SrcAtop);
    // g2.setClip(shape);
    g2.fill(new Ellipse2D.Double(cx, cy, r2, r2));
  }
  g2.dispose();
}

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

2019/01/30

Swap the position of the child components in the JSplitPane

Code

JSplitPane sp = new JSplitPane();
sp.setLeftComponent(new JScrollPane(new JTree()));
sp.setRightComponent(new JScrollPane(new JTable(6, 3)));
sp.setResizeWeight(.4);

JButton button = new JButton("swap");
button.setFocusable(false);
button.addActionListener(e -> {
  Component left = sp.getLeftComponent();
  Component right = sp.getRightComponent();

  // sp.removeAll(); // Divider is also removed
  sp.remove(left);
  sp.remove(right);
  // or: https://stackoverflow.com/questions/4871874/java-problem-with-jsplitpane
  // sp.setLeftComponent(null);
  // sp.setRightComponent(null);

  sp.setLeftComponent(right);
  sp.setRightComponent(left);

  sp.setResizeWeight(1d - sp.getResizeWeight());
  if (check.isSelected()) {
    sp.setDividerLocation(sp.getDividerLocation());
  }
});

References