package BulletGame$1;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.swing.JFrame;
import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PImage;
import processing.opengl.PGraphicsOpenGL;
import TaiGameCore.GameDataBase;
import TaiGameCore.P5GLExtend;
import TaiGameCore.PressActionThreshold;
import TaiGameCore.PressTypeThreshold;
import TaiGameCore.TaiImgMap;
import TaiGameCore.TaiScriptEditor;
import TaiGameCore.TaiScriptTxtInfo;
import TaiGameCore.TaiVBO;
public abstract class BulletGame$1Engine$L1$1$OpenglTextRenderer extends
BulletGame$1Engine$ABasicEngine {
public BulletGame$1Engine$L1$1$OpenglTextRenderer(JFrame holder,
PApplet hold) {
super(holder, hold);
}
public class EditorTextSheet extends KeyAdapter {
private Rectangle2D.Float textR;
public EditorTextSheet(Rectangle2D.Float usableSpace, int linesToShow) {
this(usableSpace, linesToShow, true, true, true, 1024, 16);
}
public EditorTextSheet(Rectangle2D.Float usableSpace, int linesToShow,
boolean b, boolean c, boolean d) {
this(usableSpace, linesToShow, b, c, d, 1024, 16);
}
public EditorTextSheet(Rectangle2D.Float usableSpace, int linesToShow,
boolean drawBG, boolean editable, boolean showsemicolon,
int FontStorageSize, int numberOfGlyphsSqrt) {
this.drawBG = drawBG;
this.editable = editable;
this.dontDrawSemicolons = !showsemicolon;
// well.
textR = usableSpace;
tse = new TaiScriptEditor("");
this.linesToShow = this.linesToShow480 = linesToShow;
renderedGlyphLocations = new ArrayList();
rowNoise = new float[linesToShow];
long myNoiseSeed = 109243;
g.noiseSeed(myNoiseSeed);
float noiseScale = 20;
g.noiseDetail(10, .5f);
for (int k = 0; k < rowNoise.length; k++) {
rowNoise[k] = 50 * g.noise(k / 20f * noiseScale);
}
//Shader initialization
GL2 gl = ((PGraphicsOpenGL) g.g).gl;
FontGlyphStorage = new TaiImgMap(numberOfGlyphsSqrt
* numberOfGlyphsSqrt, FontStorageSize);
TextBuffer = new TaiVBO(gl);
for (Shader1VertShader attrib : Shader1VertShader.values()) {
TextBuffer.registerAttrib(attrib, attrib.Type,
attrib.attribNum, attrib.attribOff);
}
}
public void useTSE(TaiScriptEditor target) {
this.tse = target;
WindowLine = Math.max(0, Math.min(tse.CaretLine, tse.Editing.size()
- linesToShow));
isTextModified = true;
textWasModifiedThisFrame = true;
}
private PFont TextFont;
public String defaultToFont = "SansSerif";
public int defaultToFontSize = 24;
private boolean selectable = true;
private float TXTSCL = 1.0f;
private boolean editable = true;
private boolean drawBG = true;
private float RowHeight;
private float LEFT_TXT_INDENT = .01f;
private int linesToShow, linesToShow480; // Less may actually be "on screen".
private float[] rowNoise;
private boolean dontDrawSemicolons = false;
private float GL_X = 0, GL_Y = 0; // UPDATE ON ALL TRANSLATIONS.
private boolean cleanedUp = false;
public void cleanup() {
cleanedUp = true;
// FontGlyphStorage.cleanup(); PImages are cleanedup via finalize.
FontGlyphStorage.cleanup((PGraphicsOpenGL) g.g);
//Don't clean up the myShaders! Those are cached.
FontGlyphStorage = null;
TextBuffer = null;
}
public void finalize() {
if (!cleanedUp) {
throw new RuntimeException(
"I was not cleaned up (EditTextScreeN)!!!" + " "
+ tse.Editing);
}
}
TaiImgMap FontGlyphStorage;
TaiVBO TextBuffer;
public boolean hasMouseFocus = true;
public float getRowHeight() {
return (RowHeight=1.f / (linesToShow480 * g.height / 480f));
}
public void draw() {
RowHeight = getRowHeight();
linesToShow = (int) (1f / RowHeight);
for (; renderedGlyphLocations.size() < linesToShow; ){
renderedGlyphLocations.add(new ArrayList());
}
GL2 gl = ((PGraphicsOpenGL) g.g).gl;
float textDistance = 4; // Sadly, my text renderer doesn't support
// perspective yet.
if (drawBG) {
g.pushMatrix();
for (int k = WindowLine; k < WindowLine + linesToShow; k++) {
float oldH = -textDistance;// sin(k/5f)*2-4;
float newH = -textDistance * 1;// sin((k+1)/5f)*2-4;
drawPaperBackgroundRow(k, oldH, newH);
g.translate(0, RowHeight);
}
g.popMatrix();
}
if (false) { // Show the text map.
g.tint(255, 255, 255, 255);
g.image(FontGlyphStorage.getCombinedImage(), 0, 0, 1, 1);
}
g.g.flush();
gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
screen2D4GL(640, 480);
GL_X = 0;
GL_Y = 0;
handleFrameModificationLogic();
int k = WindowLine;
gl.glPushMatrix();
for (; k < WindowLine + linesToShow && k < tse.Editing.size(); k++) {
float oldH = -textDistance;// sin(k/5f)*2-4;
float newH = -textDistance * 1;// sin((k+1)/5f)*2-4;
GL_X += LEFT_TXT_INDENT; // A little offset.
textRow(k, oldH, newH, isTextModified, GL_X, GL_Y, 1);
GL_X = 0;
gl.glTranslatef(0, -GL_Y + (GL_Y += RowHeight), 0);
}
gl.glPopMatrix();
renderToGL();
screen2D();
if (arrowScroll != null) {
float rh = Math.max(RowHeight, .11f);
float arrowWidth = rh, arrowHeight = rh * 1.8f;
boolean showScrollUpArrow = WindowLine > 0;
boolean showScrollDownArrow = (tse.Editing.size() - WindowLine) * RowHeight > 1;
if (showScrollUpArrow) {
g.pushMatrix();
g.translate(1 - arrowWidth, arrowHeight);
g.scale(1, -1);
g.image(FILE_SYSTEM.getImg(arrowScroll), 0f, 0f,
arrowWidth, arrowHeight);
g.popMatrix();
}
if (showScrollDownArrow) {
g.pushMatrix();
g.translate(1 - arrowWidth, 1 - arrowHeight);
g.image(FILE_SYSTEM.getImg(arrowScroll), 0f, 0f,
arrowWidth, arrowHeight);
g.popMatrix();
}
}
}
/**
* To be called once each frame, before rendering text.
*/
private void handleFrameModificationLogic() {
// isTextModified |= g.mousePressed;
isTextModified |= isResized; // The coordinates change.
if (tse.CaretLine >= WindowLine + linesToShow) {
isTextModified = true; // New perspectives!
WindowLine = tse.CaretLine - (linesToShow - 1);
}
if (tse.CaretLine < WindowLine) {
isTextModified = true;// New perspectives!
WindowLine = tse.CaretLine;
}
if (isTextModified) {
TextBuffer.reset();
}
}
/**
* Pushes the text buffer to the screen.
*/
private void renderToGL(){
GL2 gl = ((PGraphicsOpenGL) g.g).gl;
textWasModifiedThisFrame = isTextModified;
isTextModified = false;
//gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
// So, draw the TEXT on top of the other stuff:
boolean oldVal = ((PGraphicsOpenGL) g.g).MAKE_MIPMAPS;
((PGraphicsOpenGL) g.g).MAKE_MIPMAPS = false;
(((PGraphicsOpenGL) g.g)).bindTexture(FontGlyphStorage
.getCombinedImage());
((PGraphicsOpenGL) g.g).MAKE_MIPMAPS = oldVal;
// int id =
// ((PGraphicsOpenGL.ImageCache)smiley.getCache(g.g)).tindex;
gl.glEnable(GL.GL_TEXTURE_2D);
TextBuffer.CurrentColor[0] = g.red(g.g.fillColor) / 255.f;
TextBuffer.CurrentColor[1] = g.green(g.g.fillColor) / 255.f;
TextBuffer.CurrentColor[2] = g.blue(g.g.fillColor) / 255.f;
TextBuffer.drawQueuedElements();
gl.glDisable(GL.GL_TEXTURE_2D);
}
private float[] mouseLoc = new float[2];
public String arrowScroll = null;
public void setArrowScrollGraphic(String arrow) {
arrowScroll = arrow;
}
/**
* Pooled float arrays for ngonAddition
*/
private float[] textRow0 = new float[4];
private float[] textRow1 = new float[4];
private float[] textRow2 = new float[4];
private float[] textRow3 = new float[4];
private float[] textRow4 = new float[4];
private float[] writeFloats(float[] arr, float... values) {
for (int k = 0; k < values.length; k++) {
arr[k] = values[k];
}
return arr;
}
private void textRow(final int k, final float oldH, final float newH,
final boolean addToShader, float GL_X, float GL_Y, float maxW) {
mouseLoc[0] =
(g.mouseX / (float) g.width - textR.x)
/ (float) textR.width;
mouseLoc[1] =
(g.mouseY / (float) g.height - textR.y)
/ (float) textR.height;
long now = System.nanoTime();
float GL_X_0 = GL_X;
GL2 gl = ((PGraphicsOpenGL) g.g).gl;
int bestIndex = -1;
float[] highlightColor = new float[] { 31 / 255f, 79 / 255f,
179 / 255f };
if (addToShader) {
// Clear location cache
clearXcoordinates(k);
}
if (tse.Editing.size() <= k)
return;
int charIndex = 0;
char lastChar = 0;
for (char c : tse.Editing.get(k).toCharArray()) {
boolean addToShaderTmp = addToShader;
boolean lineEnd = false;
boolean skipDraw = false;
if (GL_X-GL_X_0 > maxW) { //Over.
lineEnd = true;
skipDraw = true;
}
if (tse.CaretLine == k && tse.CaretPosition == charIndex
&& selectable) {
gl.glColor3f(255, 0, 0);
gl.glBegin(gl.GL_LINES);
gl.glVertex2f(0 + GL_X, 0);
gl.glVertex2f(0 + GL_X, RowHeight);
gl.glEnd();
}
if (c == TaiScriptEditor.LINE_END_SUBCHAR) {
c = ';';
lineEnd = true;
//Alright, did this line end with {, or }?
if (lastChar == '{' || lastChar == '}') {
skipDraw = true;
}
if (dontDrawSemicolons) {
skipDraw = true;
}
}
float leftBound = GL_X;
if (!skipDraw) {
PFont TextFontTmp = TextFont;
if (TextFontTmp == null) {
/**
* IF it can't find it, it returns a font that only
* contains '?'
**/
char lookup = c;
if (Character.isWhitespace(c)) {
lookup = '!';
}
TextFontTmp = FILE_SYSTEM.getPFontFor(defaultToFont,
lookup, defaultToFontSize);
}
int index = -1;
int multiplyLetterWidth = 1;
if (Character.isWhitespace(c)) {
index = TextFontTmp.index('a');
addToShaderTmp = false;
//Use the size of the letter 'a'
if (c == '\t') {
//Workaround for tab
multiplyLetterWidth = 4;
}
} else {
index = TextFontTmp.index(c);
if (index == -1) {
index = TextFontTmp.index('?');
}
}
if (index == -1) {
index = 0;
}
float high = (float) TextFontTmp.height[index];
float bwidth = (float) TextFontTmp.width[index];
float lextent = (float) TextFontTmp.leftExtent[index];
float textent = (float) TextFontTmp.topExtent[index];
/**
* Performance needs work.
*/
float baseLine = TextFontTmp.size + 1;
float viewscl = g.width*textR.width/640f;
float yscl = RowHeight * .8f / baseLine;
float charW = .03f * TXTSCL / viewscl / baseLine;
// System.out.println(charW);
// System.out.println(xscl+" "+yscl);
// Careful, charWToMove must aliased
float charWToMove = (TextFontTmp.setWidth[index]) * charW;
charWToMove = ((int) (charWToMove * currentViewPortWidth))
/ (float) currentViewPortWidth
* multiplyLetterWidth;
if (selectable
&& tse.Selection.isInsideRegion(k, charIndex)) {
// Single-character highlights are slow.
if (tse.Selection.LineBegin == k
|| tse.Selection.LineEnd == k) {
gl.glColor3f(highlightColor[0], highlightColor[1],
highlightColor[2]);
gl.glBegin(gl.GL_QUADS);
gl.glVertex3f(0 + GL_X, 0, oldH);
gl.glVertex3f(charWToMove + GL_X, 0, oldH);
gl.glVertex3f(charWToMove + GL_X, RowHeight, newH);
gl.glVertex3f(0 + GL_X, RowHeight, newH);
gl.glEnd();
}
}
markXcoordinate(k, charIndex, GL_X);
if (addToShaderTmp) {
float charLeft = charW * (lextent);
float charRight = charW * (lextent + bwidth);
float dispHRatio = (baseLine - textent) * yscl;
float bottomHRatio = dispHRatio + (high) * yscl;
PImage img = TextFontTmp.images[index];
// Calculate the image positiosn
float imgWidth = ((float) bwidth)
/ FontGlyphStorage.width;
float imgHeight = ((float) high)
/ FontGlyphStorage.height;
//PERFORMANCE!
float[] position = textRow0;
FontGlyphStorage.addImage(img, position);
float[] toMapTo = writeFloats(textRow1, charLeft,
dispHRatio, charRight, bottomHRatio);
float[] center = writeFloats(textRow2,
(toMapTo[0] + toMapTo[2]) / 2,
(toMapTo[1] + toMapTo[3]) / 2);
float[] sizes = writeFloats(textRow3, toMapTo[2]
- toMapTo[0], toMapTo[3] - toMapTo[1]);
// Texture info.
float[] chunk = writeFloats(textRow4, position[0],
position[1], imgWidth, imgHeight);
//ADD!
TextBuffer.addNgon(4,
Shader1VertShader.Center_X, GL_X + center[0],
Shader1VertShader.Center_Y, GL_Y + center[1],
Shader1VertShader.RectWidth, sizes[0],
Shader1VertShader.RectHeight, sizes[1],
Shader1VertShader.Rotation, 0f,
Shader1VertShader.X_TexOffset, chunk[0],
Shader1VertShader.Y_TexOffset, chunk[1],
Shader1VertShader.TexScaleX, chunk[2],
Shader1VertShader.TexScaleY, chunk[3]
// Shader1VertShader.tint, Txtcolor
);
}
GL_X += charWToMove; // Getting rid of matrix
// operations in these
// loops!!!
// Next char:
if (!Character.isWhitespace(c)) {
lastChar = c;
}
}
if (lineEnd) { // Fill in the lines.
float offsetLeft = GL_X;
if (!(tse.Selection.LineBegin == k || tse.Selection.LineEnd == k)) {
// Fill in whole line.
offsetLeft = LEFT_TXT_INDENT;
}
if (selectable
&& tse.Selection.isInsideRegion(k, charIndex)) { // Rest-of-line
// highlight
gl.glColor3f(highlightColor[0], highlightColor[1],
highlightColor[2]);
gl.glBegin(gl.GL_QUADS);
gl.glVertex3f(0 + offsetLeft, 0, oldH);
gl.glVertex3f(1 + offsetLeft, 0, oldH); // Just
// very
// very
// long,
// please.
gl.glVertex3f(1 + offsetLeft, RowHeight, newH);
gl.glVertex3f(0 + offsetLeft, RowHeight, newH);
gl.glEnd();
}
}
float[] topBotBound = new float[] { GL_Y, GL_Y + RowHeight };
// Mouse over?
if (mouseLoc[0] >= leftBound && mouseLoc[1] >= topBotBound[0]
&& mouseLoc[1] < topBotBound[1]) {
// System.out.println(charIndex+" "+GL_Y+" "+GL_X+" "+Arrays.toString(mouseLoc)+" "+Arrays.toString(topBotBound));
bestIndex = charIndex;
}
charIndex++;
}
if (bestIndex != -1) {
if (g.mousePressed && hasMouseFocus) {
if (draggingSelection < 0) {
if (truth(keyboard.get(KeyEvent.VK_SHIFT))) {
draggingSelection = 1;
} else {
tse.CaretLine = k;
tse.CaretPosition = bestIndex;
draggingSelection = 0.1;
beginDraggingSelection = System.nanoTime();
tse.Selection = new TaiScriptTxtInfo(k, bestIndex,
k, bestIndex);
tse.Selection.TemporaryDisable = true;
}
}
if (draggingSelection < 1) {
draggingSelection = (System.nanoTime() - beginDraggingSelection) / 1e9 / .1; // So,
// .1
// second
} else {
if (tse.Selection.TemporaryDisable) {
if (k != tse.Selection.LineBegin
|| bestIndex != tse.Selection.CharBegin) {
tse.Selection.TemporaryDisable = false;
}
}
if (!tse.Selection.TemporaryDisable) {
tse.CaretLine = k;
tse.CaretPosition = bestIndex;
//Very good, highlighting selection puts new cursor at new dragged edge of selection.
tse.Selection = new TaiScriptTxtInfo(tse.Selection.LineBegin, tse.Selection.CharBegin,
tse.CaretLine, tse.CaretPosition);
}
}
} else {
draggingSelection = -1;
}
}
// System.out.println((System.nanoTime()-now)/1e9);
}
private long beginDraggingSelection;
private double draggingSelection = -1;
private void drawPaperBackgroundRow(int k, float oldH, float newH) {
g.noStroke();
//push fillcolor
int color = g.g.fillColor;
g.fill(rowNoise[k % rowNoise.length]);
g.beginShape();
g.vertex(0, 0, oldH);
g.vertex(1, 0, oldH);
g.vertex(1, RowHeight, newH);
g.vertex(0, RowHeight, newH);
g.endShape();
g.fill(color);
}
public boolean isTextModified = true;
public boolean textWasModifiedThisFrame = true;
public void keyPressed(KeyEvent e) {
if (!editable && !selectable) {
return;
}
if (e.getID() == KeyEvent.KEY_RELEASED) {
arrowKeysText.release();
return;
}
if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
if (e.isControlDown()) {
switch (e.getKeyCode()) {
case KeyEvent.VK_C:
tse.copy();
break;
case KeyEvent.VK_X:
if (!tse.Selection.isEmpty()) {
tse.copy();
tse.backspace();
isTextModified = true;
}
break;
case KeyEvent.VK_V:
if (editable) {
tse.paste();
isTextModified = true;
}
break;
case KeyEvent.VK_A:
tse.selectAll();
break;
}
} else if (e.getID() == KeyEvent.KEY_TYPED && editable) {
switch (e.getKeyCode()) {
case KeyEvent.VK_BACK_SPACE:
tse.backspace();
break;
case KeyEvent.VK_ENTER:
tse.line();
break;
case KeyEvent.VK_TAB:
tse.tab();
break;
case KeyEvent.VK_DELETE:
if (tse.Selection.TemporaryDisable) {
int old1 = tse.CaretLine, old2 = tse.CaretPosition;
tse.caretRight();
// If we actually moved
if (old1 != tse.CaretLine
|| old2 != tse.CaretPosition)
tse.backspace();
} else {
tse.backspace();
}
break;
case KeyEvent.VK_CONTROL:
break;
case KeyEvent.VK_ESCAPE:
break;
default:
tse.insert(e.getKeyChar());
}
isTextModified = true;
} else {
int beforeL = tse.CaretLine, beforeC = tse.CaretPosition; // May
// not
// be
// used.
switch (e.getKeyCode()) {
case KeyEvent.VK_END:
tse.CaretPosition = tse.Editing.get(tse.CaretLine)
.length() - 1;
break;
case KeyEvent.VK_HOME:
tse.CaretPosition = 0;
break;
case KeyEvent.VK_LEFT:
if (arrowKeysText.isTypeTime(KeyEvent.VK_LEFT))
tse.caretLeft();
break;
case KeyEvent.VK_RIGHT:
if (arrowKeysText.isTypeTime(KeyEvent.VK_RIGHT))
tse.caretRight();
break;
case KeyEvent.VK_DOWN:
if (arrowKeysText.isTypeTime(KeyEvent.VK_DOWN))
tse.caretDown();
break;
case KeyEvent.VK_UP:
if (arrowKeysText.isTypeTime(KeyEvent.VK_UP))
tse.caretUp();
break;
case KeyEvent.VK_PAGE_DOWN:
if (arrowKeysText.isTypeTime(KeyEvent.VK_PAGE_DOWN)) {
for (int k = 0; k < linesToShow; k++)
tse.caretDown();
}
break;
case KeyEvent.VK_PAGE_UP:
if (arrowKeysText.isTypeTime(KeyEvent.VK_PAGE_UP)) {
for (int k = 0; k < linesToShow; k++)
tse.caretUp();
}
break;
default:
}
// If we moved somewhere, affect selection.
if (tse.CaretLine != beforeL
|| tse.CaretPosition != beforeC) {
if (truth(keyboard.get(KeyEvent.VK_SHIFT))) {
if (tse.Selection.TemporaryDisable) {
tse.Selection = new TaiScriptTxtInfo(beforeL,
beforeC, beforeL, beforeC);
}
// Set the end of the selection to be the "new"
// cursor.
tse.Selection = new TaiScriptTxtInfo(
tse.Selection.LineBegin,
tse.Selection.CharBegin, tse.CaretLine,
tse.CaretPosition);
} else {
singleWidthSelect();
}
}
}
} else {
}
}
/**
* Sets the selection to be the empty selection, at the current
* tse.CaretLine, tse.CaretPosition
*/
public void singleWidthSelect() {
tse.Selection = new TaiScriptTxtInfo(tse.CaretLine,
tse.CaretPosition, tse.CaretLine, tse.CaretPosition);
tse.Selection.TemporaryDisable = true;
}
private PressTypeThreshold arrowKeysText = new PressTypeThreshold(.2,
.03);
private int WindowLine = 0;
public int getLinesToShow() {
return linesToShow;
}
public int getWindowLine() {
return WindowLine;
}
public TaiScriptEditor tse;
/**
* 0 means unlimited.
*
* Restricts the number of lines to 'i' and the number of columns to 'j'.
*/
public void setTextRestrictions(int i, int j) {
tse.sizeRestrictions(i, j);
}
public void setDisplaysLineEnd(boolean displayLineEnd) {
dontDrawSemicolons = !displayLineEnd;
}
public String getRowText(int i) {
//Hmm, should I not return the line end char?
String value = tse.Editing.get(i);
value = value.substring(0, value.length() - 1); //LINE_END is a character
return value;
}
private void markXcoordinate(int rowNumber, int glyphId, float value) {
int k = rowNumber - WindowLine;
while (glyphId >= renderedGlyphLocations.get(k).size()) {
renderedGlyphLocations.get(k).add(0f);
}
renderedGlyphLocations.get(k).set(glyphId, value);
}
private void clearXcoordinates(int rowNumber) {
int k = rowNumber - WindowLine;
renderedGlyphLocations.get(k).clear();
}
/**
* Gets the x-coordinate of glypy <i>glyphId</i> of the text row
* <i>rowNumber</i>.
*/
public float getXcoordinate(int rowNumber, int glyphId) {
if (rowNumber < WindowLine || rowNumber >= WindowLine + linesToShow) {
throw new ArrayIndexOutOfBoundsException("Text row "
+ rowNumber
+ " was not rendered this frame! call draw() first.");
}
if (glyphId < 0 || glyphId >= tse.Editing.get(rowNumber).length()) {
throw new ArrayIndexOutOfBoundsException("Invalid glyphId: "
+ glyphId);
}
int k = rowNumber - WindowLine;
if (glyphId >= renderedGlyphLocations.get(k).size()) {
return 1;
}
return renderedGlyphLocations.get(k).get(glyphId);
}
private ArrayList<ArrayList<Float>> renderedGlyphLocations;// linesToShow
public void setTextFont(PFont font) {
TextFont = font;
}
public void scaleText(float scale) {
TXTSCL = scale;
}
public void setSelectable(boolean b) {
selectable = b;
}
}
public class TaiTextBox {
public EditorTextSheet ets;
public EditorTextSheet getText() {
return ets;
}
public Rectangle2D.Float area;
public TaiTextBox(double x, double y, double w, double h, int numLines) {
this(new Rectangle2D.Float((float) x, (float) y, (float) w,
(float) h), numLines);
}
public TaiTextBox(Rectangle2D.Float area, int numLines) {
this(area, numLines, false);
}
public TaiTextBox(Rectangle2D.Float area, int numLines, boolean editable) {
this(area, numLines, editable, 256, 8);
}
public TaiTextBox(Rectangle2D.Float area, int numLines,
boolean editable, int size, int numGlypsSqrt) {
this.area = area;
if (!editable) {
ets = new EditorTextSheet(area, numLines, false, false, false,
size, numGlypsSqrt);
ets.setSelectable(false);
} else {
ets = new EditorTextSheet(area, numLines, false, true, false,
size, numGlypsSqrt);
ets.setSelectable(true);
}
ets.setArrowScrollGraphic(null);
ets.setTextRestrictions(numLines, 0);
addSubKeyListener(ets);
this.editable = editable;
postConstructor();
}
public void setArea(Rectangle2D.Float area) {
this.area = area;
ets.textR = area;
}
private boolean editable;
public boolean isEditable() {
return editable;
}
public void postConstructor() {
}
public void useFont(PFont arcadeFont) {
ets.setTextFont(arcadeFont);
}
private float txtScl = 1;
public void setTextScale(float scl) {
txtScl = scl;
}
private String lastString = "";
public void setText(String string) {
if (lastString.equals(string)) {
return;
}
//else
lastString = string;
ets.isTextModified = true;
ets.tse.newFile();
ets.tse.insertText(string);
}
public void setTextRow(String row, int rowNum) {
if (ets.getRowText(rowNum).equals(row)) { //Can't do equals; line ending char
return;
}
//else
ets.isTextModified = true;
ets.tse.setLine(row, rowNum);
}
public void draw() {
viewport(area.x, area.y, area.width, area.height);
if (false) {
outlineViewport();
}
ets.scaleText(txtScl);
ets.draw();
viewport(0, 0, 1, 1);
}
public void beginMultitext(){
ets.handleFrameModificationLogic();
}
/**
* Draws a single line of the text buffer to a given offset coordinate.
* The area rectangle is effectively ignored in this rendering mode.
*
* This is useful because it conserves font resources by using a single text component
* to render multiple, disconnected, text boxes. To achieve this, simply write each
* individual component's text to different lines (with setTextRow), and then draw
* each with drawTextRow.
*
* Call beginMultitext before rendering lines, and finish with endMultitext
*/
public void drawMultitextRow(int linenumber, float x, float y){
ets.scaleText(txtScl);
ets.textRow(linenumber, 0, 0, ets.isTextModified, x, y, 1);
}
public void endMultitext (){
ets.renderToGL();
}
public void cleanup() {
ets.cleanup();
removeSubKeyListener(ets);
}
public float getRowHeight() {
return ets.getRowHeight();
}
}
public class SaveGameDialog extends ModalDialog {
public SaveGameDialog(BulletGameScreen parent, final Runnable doAfter,
GameDataBase saveObj, String arrowToShow) {
super(parent, new ModalDialogCallback<SaveGameDialog>() {
public void dialogFinished(SaveGameDialog self) {
self.cleanup();
doAfter.run();
}
});
hash = saveObj.hashToString();
StringBuffer neoHash = new StringBuffer();
int count = 0;
for (char k : hash.toCharArray()) {
neoHash.append(k);
if (count++ % 48 == 47) {
neoHash.append("\n");
}
}
hash = neoHash.toString();
//Certain number of
area = new Rectangle2D.Float(.1f, .05f, .8f, .8f);
innerArea = new Rectangle2D.Float(0f, .2f, 1f, .79f);
scaleRect(innerArea, area);
ets = new TaiTextBox(innerArea, 24);
ets.setTextScale(.6f);
ets.ets.setSelectable(true);
ets.ets.setTextRestrictions(0, 0);
ets.setText(hash);
ets.ets.setArrowScrollGraphic(arrowToShow);
ets.ets.tse.selectAll();
Rectangle2D.Float topArea = new Rectangle2D.Float(0f, 0f, 1f, .2f);
scaleRect(topArea, area);
display = new TaiTextBox(topArea, 3);
display.setText("Your save game is below. Copy it to your clipboard \n(make sure you select all of it, try CTRL+A), \n and press ESCAPE to return.");
}
public void cleanup() {
display.cleanup();
ets.cleanup();
}
private Rectangle2D.Float area;
private Rectangle2D.Float innerArea;
private String hash;
private TaiTextBox ets;
private TaiTextBox display;
public boolean drawDialog() {
viewport(area);
g.fill(0);
g.rect(0, 0, 1, 1);
g.fill(255);
display.draw();
ets.draw();
return false;
}
}
/**
* Maps a rectangle (0,0,- 1, 1) onto another.
* TexPlace is (first parameter) adjusted
*/
public void scaleRect(Rectangle2D.Float texPlace,
Rectangle2D.Float dialogPlace) {
texPlace.x = dialogPlace.width * texPlace.x + dialogPlace.x;
texPlace.y = dialogPlace.height * texPlace.y + dialogPlace.y;
texPlace.width *= dialogPlace.width;
texPlace.height *= dialogPlace.height;
}
/**
* UnMaps a rectangle (0,0,- 1, 1) onto another.
* TexPlace is (first parameter) adjusted
*/
public void unScaleRect(Rectangle2D.Float texPlace,
Rectangle2D.Float dialogPlace) {
texPlace.x = (texPlace.x - dialogPlace.x) / dialogPlace.width;
texPlace.y = (texPlace.y - dialogPlace.y) / dialogPlace.height;
texPlace.width /= dialogPlace.width;
texPlace.height /= dialogPlace.height;
}
public static class MouseChecker {
public MouseChecker(P5GLExtend g){
this.g2 = g;
}
private boolean isInvalidPress;
private P5GLExtend g2;
public Point2D.Float getMouse(){
return new Point2D.Float(g2.g.mouseX / (float) g2.currentViewPortWidth,
g2.g.mouseY / (float) g2.currentViewPortHeight);
}
private PressActionThreshold pat = new PressActionThreshold(.3f,20f);
public boolean hovers(Rectangle2D.Float r){
PApplet g = g2.g;
float moX = g.mouseX / (float) g2.currentViewPortWidth;
float moY = g.mouseY / (float) g2.currentViewPortHeight;
Point2D.Float mo = new Point2D.Float(moX, moY);
return r.contains(mo);
}
public boolean mouseChecker(Rectangle2D.Float r){
PApplet g = g2.g;
if (g.mousePressed){
if (hovers(r) && pat.isActionTime(true)){
if (isInvalidPress){
return false;
}
return true;
} else {
isInvalidPress = true;
}
} else {
pat.isActionTime(false);
isInvalidPress = false;
}
return false;
}
}
}