Yes indeed!
The relevant code: (UIComponentInstance extends Node, UIQuad extends Quad)
package mmo.client.ui.theme;
import java.util.LinkedList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lwjgl.opengl.GL11;
import mmo.client.ui.UIColor;
import mmo.client.ui.UIQuad;
import mmo.client.ui.font.ImageFont;
import com.jme.input.KeyInput;
import com.jme.input.KeyInputListener;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.state.StippleState;
import com.jme.system.DisplaySystem;
import com.jme.util.Timer;
public class UITextEntry extends UIComponentInstance implements KeyInputListener {
private static final long serialVersionUID = 1L;
private static Log logger = LogFactory.getLog(UITextEntry.class);
private static final int MAXLENGTH = 1024;
private int cursorX;
private int cursorY;
private float lastBlink;
private float blinkInterval = .5f;
private boolean cursorOn = false;
private Timer timer = Timer.getTimer();
private UIQuad cursor;
private Node textEntry = new Node("TextEntry");
private ImageFont font = null;
private ColorRGBA textColor = null;
private LinkedList<Character> textBuffer = new LinkedList<Character>();
public UITextEntry(UIComponentInstance parent, UIComponent template,
String name, int x, int y, int width, int height) {
super(parent, template, name, x, y, width, height);
cursorX = 1;
cursorY = 2;
UIElement comp = (UIElement) UserInterface.get().getComponent("A_InputCursor");
cursor = new UIQuad("cursor", comp);
attachChild(cursor);
cursor.setCullHint(CullHint.Always);
cursor.positionLowerLeft(cursorX, cursorY);
attachChild(textEntry);
font = UserInterface.get().getFont("EntryFont");
textColor = UIColor.getColor(this.getStyle("TextColor"));
StippleState ss = DisplaySystem.getDisplaySystem().getRenderer().createStippleState();
ss.setEnabled(true);
textEntry.setRenderState(ss);
textEntry.updateRenderState();
}
private void blinkCursor() {
if (cursorOn) {
cursor.setCullHint(CullHint.Always);
cursorOn = false;
} else {
cursor.setCullHint(CullHint.Never);
cursorOn = true;
}
}
@Override
public void updateWorldData(float tpf) {
super.updateWorldData(tpf);
if (hasFocus() && timer.getTimeInSeconds()-lastBlink > blinkInterval) {
lastBlink = timer.getTimeInSeconds();
blinkCursor();
}
}
@Override
public void onEnter(int X, int Y) {
// TODO Auto-generated method stub
}
@Override
public void onExit(int X, int Y) {
// TODO Auto-generated method stub
}
@Override
public void onMouseDown(int button, int x, int y) {
if (button == 0 && !hasFocus()) {
UserInterface.get().setFocusWidget(this);
logger.debug("Text box position: X:"+getWorldX()+" Y:"+getWorldY()+" W:"+getWidth()+" H:"+getHeight());
}
}
@Override
public void onMouseMove(int deltaX, int deltaY, int newX, int newY) {
// TODO Auto-generated method stub
}
@Override
public void onMouseUp(int button, int x, int y) {
// TODO Auto-generated method stub
}
@Override
public void onKey(char c, int keyCode, boolean pressed) {
if (pressed) {
if (keyCode == KeyInput.KEY_BACK && textBuffer.size() > 0) {
char todelete = textBuffer.removeLast();
if (textEntry.getQuantity() > 0 && todelete != ' ')
textEntry.detachChildAt(textEntry.getQuantity()-1);
cursorX -= font.getWidth(todelete);
cursor.positionLowerLeft(cursorX, cursorY);
} else if (font.isCharacterMapped(c) && textBuffer.size() < MAXLENGTH) {
textBuffer.add(c);
if (c != ' ') {
UIQuad character = font.getRenderer().renderChar(c, textColor);
/*StippleState ss = DisplaySystem.getDisplaySystem().getRenderer().createStippleState();
ss.setEnabled(true);
character.setRenderState(ss);
character.updateRenderState(); */
character.positionLowerLeft(cursorX, 0);
textEntry.attachChild(character);
}
cursorX += font.getWidth(c);
cursor.positionLowerLeft(cursorX, cursorY);
}
}
}
}
And the hacked LWJGLStippleState:
package com.jme.scene.state.lwjgl;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11;
import com.jme.renderer.RenderContext;
import com.jme.scene.state.StateRecord;
import com.jme.scene.state.StippleState;
import com.jme.scene.state.lwjgl.records.StippleStateRecord;
import com.jme.system.DisplaySystem;
/**
* LWJGL implementation of {@link StippleState}
* @author Christoph Luder
*/
public class LWJGLStippleState extends StippleState {
private static final long serialVersionUID = 1L;
/**
* <code>apply</code>
* @see com.jme.scene.state.StippleState#apply()
*/
@Override
public void apply() {
// ask for the current state record
RenderContext<?> context = DisplaySystem.getDisplaySystem().getCurrentContext();
StippleStateRecord record = (StippleStateRecord) context.getStateRecord(StateType.Stipple);
context.currentStates[StateType.Stipple.ordinal()] = this;
if (isEnabled()) {
GL11.glEnable(GL11.GL_SCISSOR_TEST);
//ByteBuffer mask = getStippleMask();
GL11.glScissor(101, 6, 408, 20);
} else {
GL11.glDisable(GL11.GL_SCISSOR_TEST);
}
if (!record.isValid())
record.validate();
}
/**
* creates a new StippleStateRecord
*/
@Override
public StateRecord createStateRecord() {
return new StippleStateRecord();
}
}
I've verified the scissor coordinates are correct. The code above does no clipping. But, if I uncomment the block where I attach the StippleState to each character (a Quad), then it works perfectly.
Screenshot of the StippleState applied to the Node:
Screenshot of the StippleState applied to the Node and the child Quads:
One thing to note, is that the scissor state doesn't clip my cursor, which is expected since the cursor quad is attached directly to the grandparent Node which doesn't have a StippleState applied. However, what is unusual is that the cursor quad is leaving ghost images. I've not seen that happen before.