JTextComponentの文字列選択ハイライトのすべての角を丸めて半透明で描画する
Total: 40
, Today: 8
, Yesterday: 7
Posted by aterai at
Last-modified:
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);
int rgba = c.getSelectionColor().getRGB() & 0xFF_FF_FF | (64 << 24);
g2.setColor(new Color(rgba, true));
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
に設定
- 角の丸め方法は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(...)
をオーバーライドして選択開始行、終了行の行幅全体を含む領域を再描画している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で選択ハイライトの描画範囲を変更する