Google Tag Manager

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

2014/05/01

JProgressBar in JTable cell render a progress string

Code

class Task extends SwingWorker<Integer, ProgressValue> {
  private final int lengthOfTask;
  private final int randomSleep = new Random().nextInt(100) + 1;
  public Task(int lengthOfTask) {
    super();
    this.lengthOfTask = lengthOfTask;
  }

  @Override protected Integer doInBackground() {
    int current = 0;
    while (current < lengthOfTask && !isCancelled()) {
      current++;
      try {
        Thread.sleep(randomSleep);
      } catch (InterruptedException ie) {
        break;
      }
      publish(new ProgressValue(lengthOfTask, current));
    }
    return randomSleep * lengthOfTask;
  }
}

class ProgressValue {
  private final Integer progress;
  private final Integer lengthOfTask;
  public ProgressValue(Integer lengthOfTask, Integer progress) {
    this.progress = progress;
    this.lengthOfTask = lengthOfTask;
  }

  public Integer getProgress() {
    return progress;
  }

  public Integer getLengthOfTask() {
    return lengthOfTask;
  }
}

class ProgressRenderer extends DefaultTableCellRenderer {
  private final JProgressBar progress = new JProgressBar();
  private final JPanel renderer = new JPanel(new BorderLayout());
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    Component c;
    renderer.removeAll();
    progress.setValue(0);
    if (value instanceof ProgressValue) {
      ProgressValue pv = (ProgressValue) value;
      Integer current = pv.getProgress();
      Integer lengthOfTask = pv.getLengthOfTask();
      if (current < 0) {
        c = super.getTableCellRendererComponent(
            table, "Canceled", isSelected, hasFocus, row, column);
      } else if (current < lengthOfTask) {
        progress.setValue(current * 100 / lengthOfTask);
        progress.setStringPainted(true);
        progress.setString(String.format("%d/%d", current, lengthOfTask));
        renderer.add(progress);
        c = renderer;
      } else {
        c = super.getTableCellRendererComponent(
                table, "Done", isSelected, hasFocus, row, column);
      }
    } else {
      c = super.getTableCellRendererComponent(
              table, "Waiting...", isSelected, hasFocus, row, column);
    }
    return c;
  }

  @Override public void updateUI() {
    super.updateUI();
    setOpaque(true);
    if (Objects.nonNull(renderer)) {
      SwingUtilities.updateComponentTreeUI(renderer);
    }
  }
}

References

2014/03/27

Long pressing the JButton to get a JPopupMenu

Code

class PressAndHoldHandler extends AbstractAction implements MouseListener {
  public final JPopupMenu pop = new JPopupMenu();
  public final ButtonGroup bg = new ButtonGroup();
  private AbstractButton arrowButton;
  private final Timer holdTimer = new Timer(1000, e -> {
    if (arrowButton != null && arrowButton.getModel().isPressed()
        && holdTimer.isRunning()) {
      holdTimer.stop();
      pop.show(arrowButton, 0, arrowButton.getHeight());
      pop.requestFocusInWindow();
    }
  });

  public PressAndHoldHandler() {
    super();
    holdTimer.setInitialDelay(1000);
    pop.setLayout(new GridLayout(0, 3, 5, 5));
    for (MenuContext m: makeMenuList()) {
      AbstractButton b = new JRadioButton(m.command);
      b.setActionCommand(m.command);
      b.setForeground(m.color);
      b.setBorder(BorderFactory.createEmptyBorder());
      b.addActionListener(e -> {
        System.out.println(e.getActionCommand());
        pop.setVisible(false);
      });
      pop.add(b);
      bg.add(b);
    }
  }

  private List<MenuContext> makeMenuList() {
    return Arrays.asList(
      new MenuContext("BLACK", Color.BLACK),
      new MenuContext("BLUE", Color.BLUE),
      new MenuContext("CYAN", Color.CYAN),
      new MenuContext("GREEN", Color.GREEN),
      new MenuContext("MAGENTA", Color.MAGENTA),
      new MenuContext("ORANGE", Color.ORANGE),
      new MenuContext("PINK", Color.PINK),
      new MenuContext("RED", Color.RED),
      new MenuContext("YELLOW", Color.YELLOW));
  }

  @Override public void actionPerformed(ActionEvent e) {
    System.out.println("actionPerformed");
    if (holdTimer.isRunning()) {
      ButtonModel model = bg.getSelection();
      if (model != null) {
        System.out.println(model.getActionCommand());
      }
      holdTimer.stop();
    }
  }

  @Override public void mousePressed(MouseEvent e) {
    System.out.println("mousePressed");
    Component c = e.getComponent();
    if (SwingUtilities.isLeftMouseButton(e) && c.isEnabled()) {
      arrowButton = (AbstractButton) c;
      holdTimer.start();
    }
  }

  @Override public void mouseReleased(MouseEvent e) {
    holdTimer.stop();
  }

  @Override public void mouseExited(MouseEvent e) {
    if (holdTimer.isRunning()) {
      holdTimer.stop();
    }
  }

  @Override public void mouseEntered(MouseEvent e) {
    /* not needed */
  }

  @Override public void mouseClicked(MouseEvent e) {
    /* not needed */
  }
}

References

2014/02/26

Translucent JFrame repaint

Code

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
JLabel label = new JLabel(LocalTime.now().format(formatter), SwingConstants.CENTER);
Timer timer = new Timer(100, null);
timer.addActionListener(e -> {
  label.setText(LocalTime.now().format(formatter));
  Container parent = SwingUtilities.getUnwrappedParent(label);
  if (Objects.nonNull(parent) && parent.isOpaque()) {
    repaintWindowAncestor(label);
  }
});
// ...
private void repaintWindowAncestor(JComponent c) {
  JRootPane root = c.getRootPane();
  if (root == null) {
    return;
  }
  Rectangle r = SwingUtilities.convertRectangle(c, c.getBounds(), root);
  root.repaint(r.x, r.y, r.width, r.height);
}

References

2014/01/30

Use JTree as the table of contents

Code

class TableOfContentsTreeCellRenderer extends DefaultTreeCellRenderer {
  private static BasicStroke READER = new BasicStroke(
    1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
    1f, new float[] { 1f }, 0f);
  private String pn;
  private Point pnPt = new Point();
  private int rxs, rxe, ry;
  private boolean isSynth = false;
  private final JPanel p = new JPanel(new BorderLayout()) {
    @Override protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (pn != null) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setColor(isSynth ? getForeground() : getTextNonSelectionColor());
        g2.drawString(pn, pnPt.x - getX(), pnPt.y);
        g2.setStroke(READER);
        g2.drawLine(rxs, pnPt.y, rxe - getX(), pnPt.y);
        g2.dispose();
      }
    }

    @Override public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      d.width = Short.MAX_VALUE;
      return d;
    }
  };

  public TableOfContentsTreeCellRenderer() {
    super();
    p.setOpaque(false);
  }

  @Override public void updateUI() {
    super.updateUI();
    isSynth = getUI().getClass().getName().contains("Synth");
    if (isSynth) {
      //System.out.println("XXX: FocusBorder bug?, JDK 1.7.0, Nimbus start LnF");
      setBackgroundSelectionColor(new Color(0x0, true));
    }
  }

  @Override public Component getTreeCellRendererComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row, boolean hasFocus) {
    JLabel l = (JLabel) super.getTreeCellRendererComponent(
        tree, value, selected, expanded, leaf, row, hasFocus);
    if (value instanceof DefaultMutableTreeNode) {
      DefaultMutableTreeNode n = (DefaultMutableTreeNode) value;
      Object o = n.getUserObject();
      if (o instanceof TableOfContents) {
        TableOfContents toc = (TableOfContents) o;
        FontMetrics metrics = l.getFontMetrics(l.getFont());
        int gap = l.getIconTextGap();
        int h = l.getPreferredSize().height;
        Insets ins = tree.getInsets();

        p.removeAll();
        p.add(l, BorderLayout.WEST);
        if (isSynth) p.setForeground(l.getForeground());

        pn = String.format("%3d", toc.page);
        pnPt.x = tree.getWidth() - metrics.stringWidth(pn) - gap;
        pnPt.y = (h + metrics.getAscent()) / 2;

        rxs = l.getPreferredSize().width + gap;
        rxe = tree.getWidth() - ins.right - metrics.stringWidth("000") - gap;
        ry  = h / 2;

        return p;
      }
    }
    pn = null;
    return l;
  }
}

References