Swing/SpeechBalloonToolTipTail のバックアップ(No.2)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/SpeechBalloonToolTipTail へ行く。
- 1 (2023-04-17 (月) 03:15:24)
- 2 (2023-04-18 (火) 18:52:33)
- 3 (2023-04-30 (日) 12:15:34)
- 4 (2024-03-15 (金) 17:05:05)
- category: swing folder: SpeechBalloonToolTipTail title: JTabbedPaneのツールヒントをタブ位置に対応したふきだしに変更する tags: [JToolTip, JTabbedPane] author: aterai pubdate: 2023-04-17T03:14:21+09:00 description: JTabbedPaneのタブ用ツールヒントの形状をふきだしにし、そのしっぽの方向をタブ位置に応じて変更します。 image: https://drive.google.com/uc?id=1WbgFj8zOssgmjFU6rsL0vXNgW5Lsw7OO
概要
JTabbedPane
のタブ用ツールヒントの形状をふきだしにし、そのしっぽの方向をタブ位置に応じて変更します。
Screenshot
Advertisement
サンプルコード
class BalloonToolTip extends JToolTip {
private static final int SIZE = 4;
private transient HierarchyListener listener;
private transient Shape shape;
@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.getWindowAncestor(c))
.filter(w -> w.getType() == Window.Type.POPUP)
.ifPresent(w -> w.setBackground(new Color(0x0, true)));
}
};
addHierarchyListener(listener);
setOpaque(false);
}
@Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = 24;
return d;
}
@Override protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(getBackground());
g2.fill(shape);
g2.setPaint(getForeground());
g2.draw(shape);
g2.dispose();
super.paintComponent(g);
}
public void updateBalloonShape(int l) {
Rectangle r = getVisibleRect();
r.setSize(getPreferredSize());
Path2D tail = new Path2D.Double();
double w = r.getWidth() - 1d;
double h = r.getHeight() - 1d;
double arc = 10d;
Shape bubble;
switch (l) {
case SwingConstants.LEFT:
setBorder(BorderFactory.createEmptyBorder(2, 2 + SIZE, 2, 2));
tail.moveTo(r.getMinX() + SIZE, r.getCenterY() - SIZE);
tail.lineTo(r.getMinX(), r.getCenterY());
tail.lineTo(r.getMinX() + SIZE, r.getCenterY() + SIZE);
w -= SIZE;
bubble = new RoundRectangle2D.Double(
r.getMinX() + SIZE, r.getMinY(), w, h, arc, arc);
break;
case SwingConstants.RIGHT:
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2 + SIZE));
tail.moveTo(r.getMaxX() - SIZE - 1d, r.getCenterY() - SIZE);
tail.lineTo(r.getMaxX(), r.getCenterY());
tail.lineTo(r.getMaxX() - SIZE - 1d, r.getCenterY() + SIZE);
w -= SIZE;
bubble = new RoundRectangle2D.Double(
r.getMinX(), r.getMinY(), w, h, arc, arc);
break;
case SwingConstants.BOTTOM:
setBorder(BorderFactory.createEmptyBorder(2, 2, 2 + SIZE, 2));
tail.moveTo(r.getCenterX() - SIZE, r.getMaxY() - SIZE - 1d);
tail.lineTo(r.getCenterX(), r.getMaxY());
tail.lineTo(r.getCenterX() + SIZE, r.getMaxY() - SIZE - 1d);
h -= SIZE;
bubble = new RoundRectangle2D.Double(
r.getMinX(), r.getMinY(), w, h, arc, arc);
break;
default: // case SwingConstants.TOP:
setBorder(BorderFactory.createEmptyBorder(2 + SIZE, 2, 2, 2));
tail.moveTo(r.getCenterX() - SIZE, r.getMinY() + SIZE);
tail.lineTo(r.getCenterX(), r.getMinY());
tail.lineTo(r.getCenterX() + SIZE, r.getMinY() + SIZE);
h -= SIZE;
bubble = new RoundRectangle2D.Double(
r.getMinX(), r.getMinY() + SIZE, w, h, arc, arc);
}
Area area = new Area(bubble);
area.add(new Area(tail));
shape = area;
}
}
View in GitHub: Java, Kotlin解説
JToolTip
の形状変更はJToolTipの形状を吹き出し風に変更すると同様の方法を使用JToolTip
のテキストが長くなるとJToolTip
を常に親JFrame
内に表示される方向に設定していてもHeavyWeightWindow
が使用される可能性があるのでJWindow
を透明する設定もそのまま使用している
JToolTip
が初回表示される前にJToolTip#getVisibleRect()
を実行しても正しいサイズが取得できないので、代わりにJToolTip#getPreferredSize()
を使用する必要があるJTabbedPane.TOP
- ふきだしのしっぽの三角形を上辺中央に描画し、対象タブ領域の下辺中央に三角形の頂点が接するように
JToolTip
を配置
- ふきだしのしっぽの三角形を上辺中央に描画し、対象タブ領域の下辺中央に三角形の頂点が接するように
JTabbedPane.BOTTOM
- ふきだしのしっぽの三角形を下辺中央に描画し、対象タブ領域の上辺中央に三角形の頂点が接するように
JToolTip
を配置
- ふきだしのしっぽの三角形を下辺中央に描画し、対象タブ領域の上辺中央に三角形の頂点が接するように
JTabbedPane.LEFT
- ふきだしのしっぽの三角形を左辺中央に描画し、対象タブ領域の右辺中央に三角形の頂点が接するように
JToolTip
を配置
- ふきだしのしっぽの三角形を左辺中央に描画し、対象タブ領域の右辺中央に三角形の頂点が接するように
JTabbedPane.RIGHT
- ふきだしのしっぽの三角形を右辺中央に描画し、対象タブ領域の左辺中央に三角形の頂点が接するように
JToolTip
を配置
- ふきだしのしっぽの三角形を右辺中央に描画し、対象タブ領域の左辺中央に三角形の頂点が接するように
JToolTip
の表示位置はJTabbedPane#getToolTipLocation(...)
を以下のようにオーバーライドして変更
JTabbedPane tabs = new JTabbedPane() {
private transient BalloonToolTip tip;
@Override public Point getToolTipLocation(MouseEvent e) {
int idx = indexAtLocation(e.getX(), e.getY());
String txt = idx >= 0 ? getToolTipTextAt(idx) : null;
return Optional.ofNullable(txt).map(toolTipText -> {
JToolTip tips = createToolTip();
tips.setTipText(toolTipText);
if (tips instanceof BalloonToolTip) {
((BalloonToolTip) tips).updateBalloonShape(getTabPlacement());
}
return getToolTipPoint(
getBoundsAt(idx), tips.getPreferredSize());
}).orElse(null);
}
private Point getToolTipPoint(Rectangle r, Dimension d) {
double dx;
double dy;
switch (getTabPlacement()) {
case LEFT:
dx = r.getMaxX();
dy = r.getCenterY() - d.getHeight() / 2d;
break;
case RIGHT:
dx = r.getMinX() - d.width;
dy = r.getCenterY() - d.getHeight() / 2d;
break;
case BOTTOM:
dx = r.getCenterX() - d.getWidth() / 2d;
dy = r.getMinY() - d.height;
break;
default: // case TOP:
dx = r.getCenterX() - d.getWidth() / 2d;
dy = r.getMaxY();
}
return new Point((int) (dx + .5), (int) (dy + .5));
}
@Override public JToolTip createToolTip() {
if (tip == null) {
tip = new BalloonToolTip();
tip.updateBalloonShape(getTabPlacement());
tip.setComponent(this);
}
return tip;
}
};