The
java.awt.font.TextLayout class lets you respond
to end user mouse clicks or keyboard inputs by displaying
cursors and highlight regions in the styled text at specified
locations. This lesson explains how.
The samples in this lesson support foreign language text.
In the source there is code for strong and weak
carets, which appear in
bidirectional foreign language text.
See Lesson 4: Foreign Language Support
for more information.
Hit Testing
Hit testing is mapping a graphical location on the display
to a character position in the source text. The graphical
location is often determined by end user
mouse inputs or keyboard selections, and is
represented by a caret. A TextLayout object
provides default caret shapes including angled carets for
italic and oblique glyphs. If you want to use your own
caret shapes, you can retrieve the angle and position of
the default carets from the TextLayout object
and use that information to draw your own shapes.
This sample application draws the default caret wherever the
end user clicks on the TextLayout .
The mouseClicked method uses
TextLayout.hitTestChar to return a
java.awt.font.TextHitInfo object that contains the
mouse click location (the insertion index)
in the TextLayout object.
Information returned by the TextLayout
getAscent ,
getDescent , and
getAdvance
methods is used to compute the location of the origin for the
TextLayout object so it is horizontally
and vertically centered.
Here is the complete
HitTestSample.java source code.
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
Point2D origin = computeLayoutOrigin();
graphics2D.translate(origin.getX(), origin.getY());
textLayout.draw(graphics2D, 0, 0);
Shape[] carets =
textLayout.getCaretShapes(insertionIndex);
graphics2D.setColor(STRONG_CARET_COLOR);
graphics2D.draw(carets[0]);
if (carets[1] != null) {
graphics2D.setColor(WEAK_CARET_COLOR);
graphics2D.draw(carets[1]);
}
}
private class HitTestMouseListener
extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
Point2D origin = computeLayoutOrigin();
float clickX = (float) (e.getX() - origin.getX());
float clickY = (float) (e.getY() - origin.getY());
TextHitInfo currentHit =
textLayout.hitTestChar(clickX,
insertionIndex = currentHit.getInsertionIndex();
repaint();
}
}
Selection Highlighting
A TextLayout object supports highlighting the
entire length or portion of a text string on the screen. In this
example, the text string draws with caret at the
beginning of the line. The end user can drag the mouse from
there or click somewhere else and drag to highlight the characters.
The
code uses theTextLayout.hitTestChar. and
TextLayout.getLogicalHighlightShape to compute the
endpoint and get a Shape that
represents the highlight region.
Here is the complete
SelectionSample.java
source code.
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
Point2D origin = computeLayoutOrigin();
graphics2D.translate(origin.getX(), origin.getY());
boolean haveCaret = anchorEnd == activeEnd;
if (!haveCaret) {
Shape highlight =
textLayout.getLogicalHighlightShape(
anchorEnd, activeEnd);
graphics2D.setColor(HIGHLIGHT_COLOR);
graphics2D.fill(highlight);
}
graphics2D.setColor(TEXT_COLOR);
extLayout.draw(graphics2D, 0, 0);
if (haveCaret) {
Shape[] carets =
textLayout.getCaretShapes(anchorEnd);
graphics2D.setColor(STRONG_CARET_COLOR);
graphics2D.draw(carets[0]);
if (carets[1] != null) {
graphics2D.setColor(WEAK_CARET_COLOR);
graphics2D.draw(carets[1]);
}
}
}
private class SelectionMouseMotionListener
extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e) {
Point2D origin = computeLayoutOrigin();
float clickX = (float) (e.getX() - origin.getX());
float clickY = (float) (e.getY() - origin.getY());
TextHitInfo position =
textLayout.hitTestChar(clickX, clickY);
int newActiveEnd = position.getInsertionIndex();
if (activeEnd != newActiveEnd) {
activeEnd = newActiveEnd;
repaint();
}
}
}
private class SelectionMouseListener
extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Point2D origin = computeLayoutOrigin();
float clickX = (float) (e.getX() - origin.getX());
float clickY = (float) (e.getY() - origin.getY());
TextHitInfo position =
textLayout.hitTestChar(clickX, clickY);
anchorEnd = position.getInsertionIndex();
activeEnd = anchorEnd;
repaint();
}
}
Moving the Caret
A TextLayout object supports moving through the
text string on the display one character at a time when the
end user presses the left and right arrow keys. In this next
example, an insertion offset is initially set to 0 and moved
by calling TextLayout.getNextRightHit and
TextLayout.getNextLeftHit as appropriate in response
to left and right arrow key presses.
Here is the complete
ArrowKeySample.java source code.
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
Point2D origin = computeLayoutOrigin();
graphics2D.translate(origin.getX(), origin.getY());
textLayout.draw(graphics2D, 0, 0);
Shape[] carets =
textLayout.getCaretShapes(insertionIndex);
graphics2D.setColor(STRONG_CARET_COLOR);
graphics2D.draw(carets[0]);
if (carets[1] != null) {
graphics2D.setColor(WEAK_CARET_COLOR);
graphics2D.draw(carets[1]);
}
}
private class ArrowKeyListener extends KeyAdapter {
private void handleArrowKey(boolean rightArrow) {
TextHitInfo newPosition;
if (rightArrow) {
newPosition =
textLayout.getNextRightHit(insertionIndex);
}
else {
newPosition =
textLayout.getNextLeftHit(insertionIndex);
}
if (newPosition != null) {
insertionIndex = newPosition.getInsertionIndex();
repaint();
}
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT ||
keyCode == KeyEvent.VK_RIGHT) {
handleArrowKey(keyCode == KeyEvent.VK_RIGHT);
}
}
|