X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fio%2FSwingScreen.java;h=3d0729e4feecd719b78431d93e1e54da46c9e30a;hb=2ce6dab2bbd951e6d0f09f94759efda5ee4b65ac;hp=4a602b3c272742e20de3ac02cdf9d00f8282379e;hpb=6358f6e567422c8a5e880af36bec9ea798cc2cd4;p=fanfix.git diff --git a/src/jexer/io/SwingScreen.java b/src/jexer/io/SwingScreen.java index 4a602b3..3d0729e 100644 --- a/src/jexer/io/SwingScreen.java +++ b/src/jexer/io/SwingScreen.java @@ -33,6 +33,7 @@ import java.awt.Cursor; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; @@ -42,6 +43,7 @@ import java.awt.image.BufferedImage; import java.awt.image.BufferStrategy; import java.io.InputStream; import java.util.Date; +import java.util.HashMap; import javax.swing.JFrame; import javax.swing.SwingUtilities; @@ -146,11 +148,28 @@ public final class SwingScreen extends Screen { */ private BufferStrategy bufferStrategy; + /** + * A cache of previously-rendered glyphs for blinking text, when it + * is not visible. + */ + private HashMap glyphCacheBlink; + + /** + * A cache of previously-rendered glyphs for non-blinking, or + * blinking-and-visible, text. + */ + private HashMap glyphCache; + /** * The TUI Screen data. */ SwingScreen screen; + /** + * If true, we were successful getting Terminus. + */ + private boolean gotTerminus = false; + /** * Width of a character cell. */ @@ -253,7 +272,8 @@ public final class SwingScreen extends Screen { return MYWHITE; } } - throw new IllegalArgumentException("Invalid color: " + attr.getForeColor().getValue()); + throw new IllegalArgumentException("Invalid color: " + + attr.getForeColor().getValue()); } /** @@ -280,7 +300,8 @@ public final class SwingScreen extends Screen { } else if (attr.getBackColor().equals(jexer.bits.Color.WHITE)) { return MYWHITE; } - throw new IllegalArgumentException("Invalid color: " + attr.getBackColor().getValue()); + throw new IllegalArgumentException("Invalid color: " + + attr.getBackColor().getValue()); } /** @@ -293,8 +314,8 @@ public final class SwingScreen extends Screen { setDOSColors(); // Figure out my cursor style - String cursorStyleString = System.getProperty("jexer.Swing.cursorStyle", - "underline").toLowerCase(); + String cursorStyleString = System.getProperty( + "jexer.Swing.cursorStyle", "underline").toLowerCase(); if (cursorStyleString.equals("underline")) { cursorStyle = CursorStyle.UNDERLINE; @@ -309,11 +330,13 @@ public final class SwingScreen extends Screen { try { // Always try to use Terminus, the one decent font. - ClassLoader loader = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = Thread.currentThread(). + getContextClassLoader(); InputStream in = loader.getResourceAsStream(FONTFILE); Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in); - Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22); + Font terminus = terminusRoot.deriveFont(Font.PLAIN, 20); setFont(terminus); + gotTerminus = true; } catch (Exception e) { e.printStackTrace(); // setFont(new Font("Liberation Mono", Font.PLAIN, 24)); @@ -336,6 +359,10 @@ public final class SwingScreen extends Screen { // Save the text cell width/height getFontDimensions(); + // Cache glyphs as they are rendered + glyphCacheBlink = new HashMap(); + glyphCache = new HashMap(); + // Setup triple-buffering if (SwingScreen.tripleBuffer) { setIgnoreRepaint(true); @@ -354,15 +381,24 @@ public final class SwingScreen extends Screen { Rectangle2D bounds = fm.getMaxCharBounds(gr); int leading = fm.getLeading(); textWidth = (int)Math.round(bounds.getWidth()); - textHeight = (int)Math.round(bounds.getHeight()) - maxDescent; - // This also produces the same number, but works better for ugly + // textHeight = (int)Math.round(bounds.getHeight()) - maxDescent; + + // This produces the same number, but works better for ugly // monospace. textHeight = fm.getMaxAscent() + maxDescent - leading; + if (gotTerminus == true) { + textHeight++; + } + if (System.getProperty("os.name").startsWith("Windows")) { textAdjustY = -1; textAdjustX = 0; } + if (System.getProperty("os.name").startsWith("Mac")) { + textAdjustY = -1; + textAdjustX = 0; + } } /** @@ -391,6 +427,110 @@ public final class SwingScreen extends Screen { paint(gr); } + /** + * Draw one glyph to the screen. + * + * @param gr the Swing Graphics context + * @param cell the Cell to draw + * @param xPixel the x-coordinate to render to. 0 means the + * left-most pixel column. + * @param yPixel the y-coordinate to render to. 0 means the top-most + * pixel row. + */ + private void drawGlyph(final Graphics gr, final Cell cell, + final int xPixel, final int yPixel) { + + BufferedImage image = null; + if (cell.isBlink() && !cursorBlinkVisible) { + image = glyphCacheBlink.get(cell); + } else { + image = glyphCache.get(cell); + } + if (image != null) { + gr.drawImage(image, xPixel, yPixel, this); + return; + } + + // Generate glyph and draw it. + + image = new BufferedImage(textWidth, textHeight, + BufferedImage.TYPE_INT_ARGB); + Graphics2D gr2 = image.createGraphics(); + gr2.setFont(getFont()); + + Cell cellColor = new Cell(); + cellColor.setTo(cell); + + // Check for reverse + if (cell.isReverse()) { + cellColor.setForeColor(cell.getBackColor()); + cellColor.setBackColor(cell.getForeColor()); + } + + // Draw the background rectangle, then the foreground character. + gr2.setColor(attrToBackgroundColor(cellColor)); + gr2.fillRect(0, 0, textWidth, textHeight); + + // Handle blink and underline + if (!cell.isBlink() + || (cell.isBlink() && cursorBlinkVisible) + ) { + gr2.setColor(attrToForegroundColor(cellColor)); + char [] chars = new char[1]; + chars[0] = cell.getChar(); + gr2.drawChars(chars, 0, 1, 0 + textAdjustX, + 0 + textHeight - maxDescent + textAdjustY); + + if (cell.isUnderline()) { + gr2.fillRect(0, 0 + textHeight - 2, textWidth, 2); + } + } + gr2.dispose(); + + // We need a new key that will not be mutated by invertCell(). + Cell key = new Cell(); + key.setTo(cell); + if (cell.isBlink() && !cursorBlinkVisible) { + glyphCacheBlink.put(key, image); + } else { + glyphCache.put(key, image); + } + + gr.drawImage(image, xPixel, yPixel, this); + } + + /** + * Check if the cursor is visible, and if so draw it. + * + * @param gr the Swing Graphics context + */ + private void drawCursor(final Graphics gr) { + + if (cursorVisible + && (cursorY <= screen.height - 1) + && (cursorX <= screen.width - 1) + && cursorBlinkVisible + ) { + int xPixel = cursorX * textWidth + left; + int yPixel = cursorY * textHeight + top; + Cell lCell = screen.logical[cursorX][cursorY]; + gr.setColor(attrToForegroundColor(lCell)); + switch (cursorStyle) { + default: + // Fall through... + case UNDERLINE: + gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2); + break; + case BLOCK: + gr.fillRect(xPixel, yPixel, textWidth, textHeight); + break; + case OUTLINE: + gr.drawRect(xPixel, yPixel, textWidth - 1, textHeight - 1); + break; + } + } + } + /** * Paint redraws the whole screen. * @@ -451,12 +591,10 @@ public final class SwingScreen extends Screen { // Prevent updates to the screen's data from the TApplication // threads. synchronized (screen) { - /* System.err.printf("bounds %s X %d %d Y %d %d\n", bounds, xCellMin, xCellMax, yCellMin, yCellMax); */ - Cell lCellColor = new Cell(); for (int y = yCellMin; y < yCellMax; y++) { for (int x = xCellMin; x < xCellMax; x++) { @@ -471,68 +609,14 @@ public final class SwingScreen extends Screen { || lCell.isBlink() || reallyCleared) { - lCellColor.setTo(lCell); - - // Check for reverse - if (lCell.isReverse()) { - lCellColor.setForeColor(lCell.getBackColor()); - lCellColor.setBackColor(lCell.getForeColor()); - } - - // Draw the background rectangle, then the - // foreground character. - gr.setColor(attrToBackgroundColor(lCellColor)); - gr.fillRect(xPixel, yPixel, textWidth, textHeight); - - // Handle blink and underline - if (!lCell.isBlink() - || (lCell.isBlink() && cursorBlinkVisible) - ) { - gr.setColor(attrToForegroundColor(lCellColor)); - char [] chars = new char[1]; - chars[0] = lCell.getChar(); - gr.drawChars(chars, 0, 1, xPixel + textAdjustX, - yPixel + textHeight - maxDescent - + textAdjustY); - - if (lCell.isUnderline()) { - gr.fillRect(xPixel, yPixel + textHeight - 2, - textWidth, 2); - } - } + drawGlyph(gr, lCell, xPixel, yPixel); // Physical is always updated physical[x][y].setTo(lCell); } } } - - // Draw the cursor if it is visible - if (cursorVisible - && (cursorY <= screen.height - 1) - && (cursorX <= screen.width - 1) - && cursorBlinkVisible - ) { - int xPixel = cursorX * textWidth + left; - int yPixel = cursorY * textHeight + top; - Cell lCell = screen.logical[cursorX][cursorY]; - gr.setColor(attrToForegroundColor(lCell)); - switch (cursorStyle) { - default: - // Fall through... - case UNDERLINE: - gr.fillRect(xPixel, yPixel + textHeight - 2, - textWidth, 2); - break; - case BLOCK: - gr.fillRect(xPixel, yPixel, textWidth, textHeight); - break; - case OUTLINE: - gr.drawRect(xPixel, yPixel, textWidth - 1, - textHeight - 1); - break; - } - } + drawCursor(gr); dirty = false; reallyCleared = false; @@ -624,8 +708,48 @@ public final class SwingScreen extends Screen { return; } - // Request a repaint, let the frame's repaint/update methods do the - // right thing. + if (frame.bufferStrategy != null) { + // See if it is time to flip the blink time. + long nowTime = (new Date()).getTime(); + if (nowTime > frame.blinkMillis + frame.lastBlinkTime) { + frame.lastBlinkTime = nowTime; + frame.cursorBlinkVisible = !frame.cursorBlinkVisible; + } + + Graphics gr = frame.bufferStrategy.getDrawGraphics(); + + synchronized (this) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Cell lCell = logical[x][y]; + Cell pCell = physical[x][y]; + + int xPixel = x * frame.textWidth + frame.left; + int yPixel = y * frame.textHeight + frame.top; + + if (!lCell.equals(pCell) + || ((x == cursorX) + && (y == cursorY) + && cursorVisible) + || (lCell.isBlink()) + ) { + frame.drawGlyph(gr, lCell, xPixel, yPixel); + physical[x][y].setTo(lCell); + } + } + } + frame.drawCursor(gr); + } // synchronized (this) + + gr.dispose(); + frame.bufferStrategy.show(); + // sync() doesn't seem to help the tearing for me. + // Toolkit.getDefaultToolkit().sync(); + return; + } + + // Swing thread version: request a repaint, but limit it to the area + // that has changed. // Find the minimum-size damaged region. int xMin = frame.getWidth(); @@ -677,6 +801,8 @@ public final class SwingScreen extends Screen { yMin, yMax); */ if (frame.bufferStrategy != null) { + // This path should never be taken, but is left here for + // completeness. Graphics gr = frame.bufferStrategy.getDrawGraphics(); Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin); @@ -687,6 +813,7 @@ public final class SwingScreen extends Screen { // sync() doesn't seem to help the tearing for me. // Toolkit.getDefaultToolkit().sync(); } else { + // Repaint on the Swing thread. frame.repaint(xMin, yMin, xMax - xMin, yMax - yMin); } } @@ -745,4 +872,13 @@ public final class SwingScreen extends Screen { return ((y - frame.top) / frame.textHeight); } + /** + * Set the window title. + * + * @param title the new title + */ + public void setTitle(final String title) { + frame.setTitle(title); + } + }