blob: 0084f491f9236d108420962d7578f9522186bba2 [file] [log] [blame]
package processing.app.syntax.im;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.font.TextLayout;
import processing.app.syntax.JEditTextArea;
import processing.app.syntax.TextAreaPainter;
/**
* Paint texts from input method. Text via input method are transmitted by
* AttributedCaharacterIterator. This class helps the PDE's TextAreaPainter
* to handle AttributedCaharacterIterator.
*
* For practical purposes, paint to textarea is done by TextLayout class.
* Because TextLayout class is easy to draw composing texts. (For example,
* draw underline composing texts, focus when select from candidates text.)
*
* @author Takashi Maekawa (takachin@generative.info)
*/
public class CompositionTextPainter {
private TextLayout composedTextLayout;
private int composedBeginCaretPosition = 0;
private JEditTextArea textArea;
/**
* Constructor for painter.
* @param textarea textarea used by PDE.
*/
public CompositionTextPainter(JEditTextArea textArea) {
this.textArea = textArea;
composedTextLayout = null;
}
/**
* Check the painter has TextLayout.
* If a user input via InputMethod, this result will return true.
* @param textarea textarea used by PDE.
*/
public boolean hasComposedTextLayout() {
return (composedTextLayout != null);
}
/**
* Set TextLayout to the painter.
* TextLayout will be created and set by CompositionTextManager.
*
* @see CompositionTextManager
* @param textarea textarea used by PDE.
*/
public void setComposedTextLayout(TextLayout composedTextLayout, int composedStartCaretPosition) {
this.composedTextLayout = composedTextLayout;
this.composedBeginCaretPosition = composedStartCaretPosition;
}
/**
* Invalidate this TextLayout to set null.
* If a user end input via InputMethod, this method will called from CompositionTextManager.endCompositionText
*/
public void invalidateComposedTextLayout(int composedEndCaretPosition) {
this.composedTextLayout = null;
this.composedBeginCaretPosition = composedEndCaretPosition;
//this.composedBeginCaretPosition = textArea.getCaretPosition();
}
/**
* Draw text via input method with composed text information.
* This method can draw texts with some underlines to illustrate converting characters.
*
* This method is workaround for TextAreaPainter.
* Because, TextAreaPainter can't treat AttributedCharacterIterator directly.
* AttributedCharacterIterator has very important information when composing text.
* It has a map where are converted characters and committed characters.
* Ideally, changing TextAreaPainter method can treat AttributedCharacterIterator is better. But it's very tough!!
* So I choose to write some code as a workaround.
*
* This draw method is proceeded with the following steps.
* 1. Original TextAreaPainter draws characters.
* 2. This refillComposedArea method erase previous paint characters by textarea's background color.
* The refill area is only square that width and height defined by characters with input method.
* 3. CompositionTextPainter.draw method paints composed text. It was actually drawn by TextLayout.
*
* @param gfx set TextAreaPainter's Graphics object.
* @param fillBackGroundColor set textarea's background.
*/
public void draw(Graphics gfx, Color fillBackGroundColor) {
assert(composedTextLayout != null);
Point composedLoc = getCaretLocation();
refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y);
composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y);
}
/**
* Fill color to erase characters drawn by original TextAreaPainter.
*
* @param fillColor fill color to erase characters drawn by original TextAreaPainter method.
* @param x x-coordinate where to fill.
* @param y y-coordinate where to fill.
*/
private void refillComposedArea(Color fillColor, int x, int y) {
Graphics gfx = textArea.getPainter().getGraphics();
gfx.setColor(fillColor);
FontMetrics fm = textArea.getPainter().getFontMetrics();
int newY = y - (fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT);
int paintHeight = fm.getHeight();
int paintWidth = (int) composedTextLayout.getBounds().getWidth();
gfx.fillRect(x, newY, paintWidth, paintHeight);
}
private Point getCaretLocation() {
Point loc = new Point();
TextAreaPainter painter = textArea.getPainter();
FontMetrics fm = painter.getFontMetrics();
int offsetY = fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT;
int lineIndex = textArea.getCaretLine();
loc.y = lineIndex * fm.getHeight() + offsetY;
int offsetX = composedBeginCaretPosition - textArea.getLineStartOffset(lineIndex);
loc.x = textArea.offsetToX(lineIndex, offsetX);
return loc;
}
}