Swing/RoundedSelectionHighlightPainter の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/RoundedSelectionHighlightPainter へ行く。
- Swing/RoundedSelectionHighlightPainter の差分を削除
---
category: swing
folder: RoundedSelectionHighlightPainter
title: JTextComponentの選択ハイライトを角丸で描画する
tags: [JTextComponent, DefaultCaret, Highlighter, HighlightPainter, DefaultHighlighter]
author: aterai
pubdate: 2025-03-03T01:21:51+09:00
description: JTextComponentの選択ハイライトや検索ハイライトなどを角丸で描画するHighlightPainterを作成します。
image: https://drive.google.com/uc?id=1Dqr2sjeKtmMlgwKYuFxDhckSJHlDTzHS
---
* Summary [#summary]
`JTextComponent`の選択ハイライトや検索ハイライトなどを角丸で描画する`HighlightPainter`を作成します。
#download(https://drive.google.com/uc?id=1Dqr2sjeKtmMlgwKYuFxDhckSJHlDTzHS)
* Source Code Examples [#sourcecode]
#code(link){{
class RoundedSelectionHighlightPainter extends DefaultHighlightPainter {
protected RoundedSelectionHighlightPainter() {
super(null);
}
@Override public void paint(
Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
Rectangle alloc = bounds.getBounds();
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Rectangle p0 = mapper.modelToView(c, offs0);
Rectangle p1 = mapper.modelToView(c, offs1);
// --- render ---
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = getColor();
if (color == null) {
g2.setColor(c.getSelectionColor().brighter());
} else {
g2.setColor(color);
}
if (p0.y == p1.y) { // same line, render a rectangle
Rectangle r = p0.union(p1);
g2.fillRoundRect(r.x, r.y, r.width, r.height, 5, 5);
} else { // different lines
int p0ToMarginWidth = alloc.x + alloc.width - p0.x;
g2.fillRoundRect(p0.x, p0.y, p0ToMarginWidth, p0.height, 5, 5);
g2.fillRect(p0.x + 5, p0.y, p0ToMarginWidth - 5, p0.height);
int maxY = p0.y + p0.height;
if (maxY != p1.y) {
g2.fillRect(alloc.x, maxY, alloc.width, p1.y - maxY);
}
g2.fillRect(alloc.x, p1.y, p1.x - alloc.x - 5, p1.height);
g2.fillRoundRect(alloc.x, p1.y, p1.x - alloc.x, p1.height, 5, 5);
}
g2.dispose();
} catch (BadLocationException ex) {
// can't render
Logger.getGlobal().severe(ex::getMessage);
}
}
@Override public Shape paintLayer(
Graphics g, int offs0, int offs1, Shape bounds,
JTextComponent c, View view) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = getColor();
if (color == null) {
g2.setColor(c.getSelectionColor());
} else {
g2.setColor(color);
}
Rectangle r = null;
if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
if (bounds instanceof Rectangle) {
r = (Rectangle) bounds;
} else {
r = bounds.getBounds();
}
} else {
// Should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(
offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
r = shape instanceof Rectangle ? (Rectangle) shape : shape.getBounds();
} catch (BadLocationException ex) {
// can't render
Logger.getGlobal().severe(ex::getMessage);
}
}
if (r != null) {
// If we are asked to highlight, we should draw something even
// if the model-to-view projection is of zero width (6340106).
r.width = Math.max(r.width, 1);
g2.fillRoundRect(r.x, r.y, r.width - 1, r.height - 1, 5, 5);
g2.setColor(c.getSelectionColor().darker());
g2.drawRoundRect(r.x, r.y, r.width - 1, r.height - 1, 5, 5);
}
g2.dispose();
return r;
}
}
}}
* Explanation [#explanation]
- 選択ハイライト: `RoundedSelectionHighlightPainter`
-- [[JTextComponentの選択ハイライトを変更>Swing/SelectionHighlightPainter]]と同様に、`Caret#getSelectionPainter()`メソッドをオーバーライドして選択ハイライトを描画する`HighlightPainter`を変更
-- `DefaultHighlightPainter#paintLayer(...)`をオーバーライドして`Graphics#fillRect(...)`を`Graphics#fillRoundRect(...)`に置換して各選択ハイライト領域の角を丸めて描画
-- `DefaultHighlighter#setDrawsLayeredHighlights(false)`が設定されている場合、`DefaultHighlightPainter#paint(...)`でハイライトが描画されるので、こちらをオーバーライドして選択ハイライトを角丸に変更
--- 選択が同一行内に収まる場合は`Graphics#fillRect(...)`を`Graphics#fillRoundRect(...)`に置換して角丸矩形を描画
--- 選択が複数行に渡る場合は先頭と末尾の選択行領域を描画する`Graphics#fillRect(...)`を`Graphics#fillRoundRect(...)`に置換して角丸矩形を描画
-- 選択行領域に[[JTreeのノード選択で生成された直角多角形の角を丸める>Swing/FlatTreeNodeRoundedCornerSelection]]と同様の平坦化を適用すれば、`Visual Studio Code`と同様の角丸選択を描画することも可能
--- [[JTextComponentの文字列選択ハイライトのすべての角を丸めて半透明で描画する>Swing/TranslucentRoundedSelection]]に移動
- 検索ハイライト: `RoundedHighlightPainter`
-- [[JTextAreaの検索ハイライトに縁を描画する>Swing/BorderHighlightPainter]]と同様に、`Highlighter#addHighlight(...)`メソッドで検索にヒットした文字列のハイライトを描画する`HighlightPainter`を指定
-- 選択ハイライトと同様に`DefaultHighlighter#setDrawsLayeredHighlights(...)`の設定を考慮して`DefaultHighlightPainter#paintLayer(...)`と`DefaultHighlightPainter#paint(...)`の両方をオーバーライドしてハイライトを角丸で描画
#code{{
class RoundedHighlightPainter extends DefaultHighlightPainter {
protected RoundedHighlightPainter() {
super(new Color(0x0, true));
}
@Override public void paint(
Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
try {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
TextUI mapper = c.getUI();
Rectangle p0 = mapper.modelToView(c, offs0);
Rectangle p1 = mapper.modelToView(c, offs1);
if (p0.y == p1.y) { // same line, render a rectangle
Shape s = makeRoundRectangle(p0.union(p1));
g2.setColor(Color.PINK);
g2.fill(s);
g2.setPaint(Color.RED);
g2.draw(s);
} else { // different lines
Rectangle alloc = bounds.getBounds();
int p0ToMarginWidth = alloc.x + alloc.width - p0.x;
g2.setColor(Color.PINK);
g2.fillRoundRect(p0.x, p0.y, p0ToMarginWidth, p0.height, 5, 5);
g2.fillRect(p0.x + 5, p0.y, p0ToMarginWidth - 5, p0.height);
int maxY = p0.y + p0.height;
if (maxY != p1.y) {
g2.fillRect(alloc.x, maxY, alloc.width, p1.y - maxY);
}
g2.fillRect(alloc.x, p1.y, p1.x - alloc.x - 5, p1.height);
g2.fillRoundRect(alloc.x, p1.y, p1.x - alloc.x, p1.height, 5, 5);
}
g2.dispose();
} catch (BadLocationException ex) {
// can't render
Logger.getGlobal().severe(ex::getMessage);
}
}
@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);
if (s instanceof Rectangle) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.ORANGE);
Shape r = makeRoundRectangle(s.getBounds());
g2.fill(r);
g2.setPaint(Color.RED);
g2.draw(r);
g2.dispose();
}
return s;
}
private static RoundRectangle2D makeRoundRectangle(Rectangle r) {
return new RoundRectangle2D.Float(r.x, r.y, r.width - 1, r.height - 1, 5f, 5f);
}
}
}}
* Reference [#reference]
- [[JTextComponentの選択ハイライトを変更>Swing/SelectionHighlightPainter]]
- [[DefaultHighlighterの描画方法を変更する>Swing/DrawsLayeredHighlights]]
- [[JTextAreaの検索ハイライトに縁を描画する>Swing/BorderHighlightPainter]]
* Comment [#comment]
#comment
#comment