| 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; |
| } |
| } |