Google Tag Manager

2022/06/30

Change the height of a drop-down list of JComboBox by mouse dragging

Code

JLabel bottom = new JLabel("", new DotIcon(), SwingConstants.CENTER);
MouseInputListener rwl = new ResizeWindowListener();
bottom.addMouseListener(rwl);
bottom.addMouseMotionListener(rwl);
bottom.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
bottom.setOpaque(true);
bottom.setBackground(new Color(0xE0_E0_E0));
bottom.setFocusable(false);

JPanel resizePanel = new JPanel(new BorderLayout());
resizePanel.add(scroll);
resizePanel.add(bottom, BorderLayout.SOUTH);
resizePanel.add(Box.createHorizontalStrut(240), BorderLayout.NORTH);
resizePanel.setBorder(BorderFactory.createLineBorder(new Color(0x64_64_64)));

JPopupMenu popup = new JPopupMenu();
popup.add(resizePanel);

// ...
class ResizeWindowListener extends MouseInputAdapter {
  private final Rectangle rect = new Rectangle();

  @Override public void mousePressed(MouseEvent e) {
    Window w = SwingUtilities.getWindowAncestor(e.getComponent());
    if (w != null) {
      rect.setSize(w.getSize());
    }
  }

  @Override public void mouseDragged(MouseEvent e) {
    Window w = SwingUtilities.getWindowAncestor(e.getComponent());
    if (!rect.isEmpty() && w != null) {
      rect.height += e.getY();
      w.setSize(rect.width, rect.height);
    }
  }
}

References

2022/05/30

Focus Border of the previously selected tab in JTabbedPane is thinly displayed as history

Code

class LineFocusTabbedPane extends JTabbedPane {
  private transient ChangeListener listener;

  protected LineFocusTabbedPane() {
    super();
  }

  @Override public void updateUI() {
    removeChangeListener(listener);
    UIManager.put("TabbedPane.tabInsets", new InsetsUIResource(1, 4, 0, 4));
    UIManager.put("TabbedPane.selectedTabPadInsets", new InsetsUIResource(1, 1, 1, 1));
    UIManager.put("TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2));
    UIManager.put("TabbedPane.selectedLabelShift", 0);
    UIManager.put("TabbedPane.labelShift", 0);
    UIManager.put("TabbedPane.focus", new ColorUIResource(new Color(0x0, true)));
    super.updateUI();
    listener = new TabSelectionListener();
    addChangeListener(listener);
    setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
  }

  @Override public void insertTab(String title, Icon icon, Component c, String tip, int index) {
    super.insertTab(title, icon, c, tip, index);
    JLabel label = new JLabel(title, icon, SwingConstants.CENTER);
    setTabComponentAt(index, label);
  }
}

class TabSelectionListener implements ChangeListener {
  private static final Color ALPHA_ZERO = new Color(0x0, true);
  private static final Color SELECTION_COLOR = new Color(0x00_AA_FF);
  private static final Color PREV_COLOR = new Color(0x48_00_AA_FF, true);
  private int prev = -1;

  @Override public void stateChanged(ChangeEvent e) {
    JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
    if (tabbedPane.getTabCount() <= 0) {
      return;
    }
    int idx = tabbedPane.getSelectedIndex();
    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
      Component tab = tabbedPane.getTabComponentAt(i);
      if (tab instanceof JComponent) {
        Color color;
        if (i == idx) {
          color = SELECTION_COLOR;
        } else if (i == prev) {
          color = PREV_COLOR;
        } else {
          color = ALPHA_ZERO;
        }
        ((JComponent) tab).setBorder(BorderFactory.createMatteBorder(3, 0, 0, 0, color));
      }
    }
    prev = idx;
  }
}

References

2022/04/30

Disable the arrow button when the knob position of the JScrollBar is on the boundary

Code

// UIManager.put("ScrollBar.alwaysShowThumb", Boolean.TRUE);
JScrollPane scroll = new JScrollPane(new JTable(24, 3));
scroll.getVerticalScrollBar().addAdjustmentListener(e -> {
  JScrollBar scrollBar = (JScrollBar) e.getAdjustable();
  BoundedRangeModel m = scrollBar.getModel();
  int value = m.getValue();
  boolean max = value == m.getMaximum() - m.getExtent();
  Optional.ofNullable(scrollBar.getComponent(0))
      .ifPresent(b -> b.setEnabled(!max));
  boolean min = value == m.getMinimum();
  Optional.ofNullable(scrollBar.getComponent(1))
      .ifPresent(b -> b.setEnabled(!min));
});

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

2022/02/28

Create a new JFrame when the JTabbedPane tab is dropped outside the frame

Code

class TabDragSourceListener implements DragSourceListener {
  @Override public void dragEnter(DragSourceDragEvent e) {
    e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
  }

  @Override public void dragExit(DragSourceEvent e) {
    e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
  }

  @Override public void dragDropEnd(DragSourceDropEvent e) {
    Component c = e.getDragSourceContext().getComponent();
    JRootPane root = ((JComponent) c).getRootPane();
    Class<GhostGlassPane> clz = GhostGlassPane.class;
    Optional.ofNullable(root.getGlassPane())
        .filter(clz::isInstance).map(clz::cast)
        .ifPresent(p -> p.setVisible(false));
    boolean dropSuccess = e.getDropSuccess();
    Window w = SwingUtilities.getWindowAncestor(c);
    boolean outOfFrame = !w.getBounds().contains(e.getLocation());
    if (dropSuccess && outOfFrame && c instanceof DnDTabbedPane) {
      DnDTabbedPane src = (DnDTabbedPane) c;
      int index = src.dragTabIndex;
      final Component cmp = src.getComponentAt(index);
      final Component tab = src.getTabComponentAt(index);
      final String title = src.getTitleAt(index);
      final Icon icon = src.getIconAt(index);
      final String tip = src.getToolTipTextAt(index);
      src.remove(index);
      DnDTabbedPane tabs = new DnDTabbedPane();
      tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
      tabs.addTab(title, icon, cmp, tip);
      tabs.setTabComponentAt(0, tab);
      JFrame frame = new JFrame();
      frame.getContentPane().add(tabs);
      frame.setSize(320, 240);
      frame.setLocation(e.getLocation());
      frame.setVisible(true);
    }
  }

  @Override public void dropActionChanged(DragSourceDragEvent e) {
    /* not needed */
  }

  @Override public void dragOver(DragSourceDragEvent e) {
    /* not needed */
  }
}

References