Swing/TranslucentRoundedSelection のバックアップ(No.1)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/TranslucentRoundedSelection へ行く。
- 1 (2025-03-24 (月) 03:10:09)
- 2 (2025-03-24 (月) 15:25:47)
- 3 (2025-03-24 (月) 22:07:10)
- 4 (2025-03-27 (木) 08:43:17)
- 5 (2025-03-27 (木) 20:28:04)
- category: swing folder: TranslucentRoundedSelection title: JTextComponentの文字列選択ハイライトのすべての角を丸めて半透明で描画する tags: [JTextComponent, DefaultHighlightPainter, Caret, JTextArea, JEditorPane] author: aterai pubdate: 2025-03-24T03:07:24+09:00 description: JTextAreaやJEditorPaneなどのJTextComponentが使用するCaretに文字列選択ハイライトのすべての角を丸め、かつ半透明で描画するHighlightPainterを使用するよう設定します。 image: https://drive.google.com/uc?id=1tragzwUpNp26U_PhnHyKNCzU9mtvNiZx
Summary
JTextAreaやJEditorPaneなどのJTextComponentが使用するCaretに文字列選択ハイライトのすべての角を丸め、かつ半透明で描画するHighlightPainterを使用するよう設定します。
Screenshot

Advertisement
Source Code Examples
class RoundedSelectionHighlightPainter extends DefaultHighlightPainter {
protected RoundedSelectionHighlightPainter() {
super(null);
}
@Override public void paint(
Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color color = c.getSelectionColor();
g2.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 64));
try {
Area area = getLinesArea(c, offs0, offs1);
for (Area a : GeomUtils.singularization(area)) {
List<Point2D> lst = GeomUtils.convertAreaToPoint2DList(a);
GeomUtils.flatteningStepsOnRightSide(lst, 3d * 2d);
g2.fill(GeomUtils.convertRoundedPath(lst, 3d));
}
} catch (BadLocationException ex) {
// can't render
Logger.getGlobal().severe(ex::getMessage);
}
g2.dispose();
}
private static Area getLinesArea(JTextComponent c, int offs0, int offs1)
throws BadLocationException {
TextUI mapper = c.getUI();
Area area = new Area();
int cur = offs0;
do {
int startOffset = Utilities.getRowStart(c, cur);
int endOffset = Utilities.getRowEnd(c, cur);
Rectangle p0 = mapper.modelToView(c, Math.max(startOffset, offs0));
Rectangle p1 = mapper.modelToView(c, Math.min(endOffset, offs1));
if (p0.x == p1.x) {
p0.width += 6;
addRectToArea(area, p0);
} else {
addRectToArea(area, p0.union(p1));
}
cur = endOffset + 1;
} while (cur < offs1);
return area;
}
private static void addRectToArea(Area area, Rectangle rect) {
area.add(new Area(rect));
}
}
View in GitHub: Java, KotlinExplanation
- すべての角を丸める
- JTextComponentの選択ハイライトを角丸で描画すると同様に、
DefaultHighlighter#setDrawsLayeredHighlights(false)
を設定DefaultHighlightPainter#paint(...)
をオーバーライドして選択ハイライト領域を取得して角丸に変更するHighlightPainter
を作成- この
HighlightPainter
を文字列選択で使用するCaret
を作成してJTextComponent
に設定
- 角の丸め方はJTreeのノード選択で生成された直角多角形の角を丸めると同様に、ラウンド直径より小さい段差などで尖点が発生しないよう選択領域図形を平坦化してから実施
- 行末の改行のみが選択された場合はその行の選択領域幅を
3(ラウンド半径) * 2
の6px
に拡張してから角丸に変換 - 複数行に渡って文字列選択されている場合、
DefaultHighlighter#setDrawsLayeredHighlights(false)
を設定した場合のデフォルトであるJTextComponent
の行幅全体をハイライトするのではなく、文字列部分のみ選択ハイライトするよう変更するため、Utilities.getRowStart(...)
、Utilities.getRowEnd(...)
で取得した行頭、行末オフセットの位置を選択ハイライト領域としている- JEditorPaneで選択ハイライトの描画範囲を変更する
JTextArea#setLineWrap(true)
やJTextArea#setWrapStyleWord(true)
が設定されて折り返しが発生する場合、Utilities.getRowEnd(...)
で取得される行末の位置は折り返しの位置になる
- デフォルトの
DefaultCaret
での選択ハイライト再描画では領域の右側の隅が更新できない場合があるので、DefaultCaret#damage(...)
をオーバーライドして選択開始行、終了行の行幅全体を含む領域を再描画している#code {{
class RoundedSelectionCaret extends DefaultCaret {
@Override protected HighlightPainter getSelectionPainter() {
return new RoundedSelectionHighlightPainter();
}
@SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel")
@Override protected synchronized void damage(Rectangle r) {
JTextComponent c = getComponent();
int startOffset = c.getSelectionStart();
int endOffset = c.getSelectionEnd();
if (startOffset == endOffset) {
super.damage(r);
} else {
TextUI mapper = c.getUI();
try {
Rectangle p0 = mapper.modelToView(c, startOffset);
Rectangle p1 = mapper.modelToView(c, endOffset);
int h = (int) (p1.getMaxY() - p0.getMinY());
c.repaint(new Rectangle(0, p0.y, c.getWidth(), h));
} catch (BadLocationException ex) {
UIManager.getLookAndFeel().provideErrorFeedback(c);
}
}
}
}
}}
- JTextComponentの選択ハイライトを角丸で描画すると同様に、
- 選択ハイライトを半透明化
- JEditorPaneで選択した文字列の色反転を無効化では
JTextComponent#setSelectedTextColor(null)
、JTextComponent#setSelectionColor(new Color(0x64_88_AA_AA, true))
を設定して選択文字色の変更は無し、選択ハイライトを半透明に設定しているが、このサンプルではDefaultCaret#getSelectionPainter()
をオーバーライドしているので、DefaultHighlightPainter#paint(...)
内で角丸化と同時に半透明化を実施している
- JEditorPaneで選択した文字列の色反転を無効化では
Reference
- JTextComponentの選択ハイライトを角丸で描画する
- JTreeのノード選択で生成された直角多角形の角を丸める
- JEditorPaneで選択した文字列の色反転を無効化
- JEditorPaneで選択ハイライトの描画範囲を変更する