feat(renderer): replace G2DTextfieldControlImpl2 with G2DSingleLineTextfieldControlImpl and enhance CustomMultiLineTextArea with cachedRawFontHeight

This commit is contained in:
extio-de
2026-03-02 22:17:03 +01:00
parent efc48b8741
commit 2091e16810
4 changed files with 770 additions and 7 deletions

View File

@@ -20,7 +20,7 @@ import de.extio.game_engine.renderer.g2d.control.impl.G2DSetFocusControl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DSliderControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DSwitchControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DTableControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DTextfieldControlImpl2;
import de.extio.game_engine.renderer.g2d.control.impl.G2DSingleLineTextfieldControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DToggleButtonControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DTooltipControl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DWindowCloseButtonControlImpl;
@@ -235,7 +235,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
control = new G2DLabelControlImpl();
}
else if (this.clazz == TextfieldControl.class) {
control = new G2DTextfieldControlImpl2();
control = new G2DSingleLineTextfieldControlImpl();
}
else if (this.clazz == MultiLineTextAreaControl.class) {
control = new G2DMultiLineTextAreaControlImpl();
@@ -366,8 +366,8 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
}
else if (this.clazz == TextfieldControl.class && this.textfieldData != null) {
((TextfieldControl) control).setCustomData(this.textfieldData);
if (control instanceof final G2DTextfieldControlImpl2 tfc2) {
tfc2.setLastCaptionUpdateTime(this.lastCaptionUpdateTime);
if (control instanceof final G2DSingleLineTextfieldControlImpl tfc) {
tfc.setLastCaptionUpdateTime(this.lastCaptionUpdateTime);
}
}
else if (this.clazz == MultiLineTextAreaControl.class && this.multiLineTextAreaData != null) {

View File

@@ -81,6 +81,8 @@ public class CustomMultiLineTextArea extends Component {
private int cachedFontSize = 0;
private int cachedRawFontHeight = 0;
private int cachedSpaceWidth = 0;
private List<String> cachedWrappedLines = null;
@@ -108,6 +110,7 @@ public class CustomMultiLineTextArea extends Component {
this.cachedLineHeight = 0;
this.cachedSpaceWidth = 0;
this.cachedFontSize = 0;
this.cachedRawFontHeight = 0;
this.cachedWrappedLines = null;
}
this.fontSize = fontSize;
@@ -934,6 +937,10 @@ public class CustomMultiLineTextArea extends Component {
final List<String> wrappedLines = getWrappedLines();
final int lineHeight = getLineHeight();
if (this.cachedRawFontHeight == 0) {
this.cachedRawFontHeight = G2DDrawFont.getTextDimensions("ÄÖÜABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzgjpqy", g2d, this.fontSize, 1.0).getY();
}
final int rawFontHeight = this.cachedRawFontHeight;
final Color fgColor = this.foregroundColor != null ? this.foregroundColor : theme.getTextNormal().toColor();
@@ -1008,15 +1015,15 @@ public class CustomMultiLineTextArea extends Component {
final String beforeCaret = caretPosInWrappedLine > 0 && caretPosInWrappedLine <= caretWrappedLine.length()
? caretWrappedLine.substring(0, caretPosInWrappedLine)
: "";
final var textDim = beforeCaret.isEmpty() ? ImmutableCoordI2.create(0, (int) (this.fontSize * this.scaleFactor)) : G2DDrawFont.getTextDimensions(beforeCaret, g2d, this.fontSize, this.scaleFactor);
final var textDim = beforeCaret.isEmpty() ? ImmutableCoordI2.create(0, 0) : G2DDrawFont.getTextDimensions(beforeCaret, g2d, this.fontSize, this.scaleFactor);
final int spaceWidth = beforeCaret.endsWith(" ") ? getSpaceWidth() : 0;
final int textHeight = textDim.getY();
final int caretX = textDim.getX() + TEXT_PADDING + (int) (spaceWidth * this.scaleFactor);
final int caretY = (int) ((caretWrappedLineIndex * lineHeight - this.scrollOffsetY) * this.scaleFactor);
final int caretHeight = (int) (rawFontHeight * this.scaleFactor);
if (caretY >= 0 && caretY < this.getHeight()) {
g2d.setColor((currentTime % 1000 < 500) ? theme.getSelectionPrimary().toColor() : theme.getSelectionSecondary().toColor());
g2d.fillRect(caretX, caretY, 2, textHeight);
g2d.fillRect(caretX, caretY, 2, caretHeight);
}
}
}

View File

@@ -0,0 +1,602 @@
package de.extio.game_engine.renderer.g2d.control.components;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.function.Consumer;
import de.extio.game_engine.renderer.ThemeManager;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
import de.extio.game_engine.renderer.g2d.theme.Theme;
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
@SuppressWarnings("serial")
public class CustomSingleLineTextField extends Component {
private static final int CARET_BLINK_INTERVAL = 500;
private static final int TEXT_PADDING = 4;
private String text = "";
private int caretPosition = 0;
private int selectionAnchor = 0;
private int scrollOffsetX = 0;
private int fontSize;
private double scaleFactor;
private boolean enabled = true;
private Color backgroundColor;
private Color foregroundColor;
private boolean dirty = true;
private boolean readonly = false;
private long lastBlinkTime = 0;
private boolean caretVisible = true;
private int cachedRawFontHeight = 0;
private final ThemeManager themeManager;
private final Consumer<String> onTextChanged;
private final Runnable onSubmit;
public void setText(final String text) {
this.text = text == null ? "" : text;
this.caretPosition = Math.min(this.caretPosition, this.text.length());
this.selectionAnchor = this.caretPosition;
this.dirty = true;
}
public String getText() {
return this.text;
}
public void setFontSize(final int fontSize) {
if (this.fontSize != fontSize) {
this.cachedRawFontHeight = 0;
}
this.fontSize = fontSize;
this.dirty = true;
}
public void setScaleFactor(final double scaleFactor) {
this.scaleFactor = scaleFactor;
this.dirty = true;
}
@Override
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
this.dirty = true;
}
public void setReadonly(final boolean readonly) {
this.readonly = readonly;
}
public void setBackgroundColor(final Color color) {
this.backgroundColor = color;
this.dirty = true;
}
public void setForegroundColor(final Color color) {
this.foregroundColor = color;
this.dirty = true;
}
public boolean isDirty() {
return this.dirty;
}
public void setDirty(final boolean dirty) {
this.dirty = dirty;
}
public CustomSingleLineTextField(final ThemeManager themeManager, final Consumer<String> onTextChanged, final Runnable onSubmit) {
this.themeManager = themeManager;
this.onTextChanged = onTextChanged;
this.onSubmit = onSubmit;
this.setIgnoreRepaint(true);
this.setFocusable(true);
this.addKeyListener(new KeyListener() {
@Override
public void keyTyped(final KeyEvent e) {
if (!CustomSingleLineTextField.this.enabled || CustomSingleLineTextField.this.readonly) {
return;
}
final char ch = e.getKeyChar();
if (!Character.isISOControl(ch)) {
deleteSelection();
insertChar(ch);
}
else if (ch == '\b') {
backspace();
}
}
@Override
public void keyPressed(final KeyEvent e) {
if (!CustomSingleLineTextField.this.enabled) {
return;
}
final boolean shiftPressed = e.isShiftDown();
final boolean ctrlPressed = e.isControlDown();
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER:
if (!CustomSingleLineTextField.this.readonly) {
if (CustomSingleLineTextField.this.onSubmit != null) {
CustomSingleLineTextField.this.onSubmit.run();
}
}
break;
case KeyEvent.VK_LEFT:
if (CustomSingleLineTextField.this.caretPosition > 0) {
CustomSingleLineTextField.this.caretPosition--;
if (!shiftPressed) {
CustomSingleLineTextField.this.selectionAnchor = CustomSingleLineTextField.this.caretPosition;
}
ensureCaretVisible();
CustomSingleLineTextField.this.dirty = true;
}
break;
case KeyEvent.VK_RIGHT:
if (CustomSingleLineTextField.this.caretPosition < CustomSingleLineTextField.this.text.length()) {
CustomSingleLineTextField.this.caretPosition++;
if (!shiftPressed) {
CustomSingleLineTextField.this.selectionAnchor = CustomSingleLineTextField.this.caretPosition;
}
ensureCaretVisible();
CustomSingleLineTextField.this.dirty = true;
}
break;
case KeyEvent.VK_HOME:
CustomSingleLineTextField.this.caretPosition = 0;
if (!shiftPressed) {
CustomSingleLineTextField.this.selectionAnchor = CustomSingleLineTextField.this.caretPosition;
}
ensureCaretVisible();
CustomSingleLineTextField.this.dirty = true;
break;
case KeyEvent.VK_END:
CustomSingleLineTextField.this.caretPosition = CustomSingleLineTextField.this.text.length();
if (!shiftPressed) {
CustomSingleLineTextField.this.selectionAnchor = CustomSingleLineTextField.this.caretPosition;
}
ensureCaretVisible();
CustomSingleLineTextField.this.dirty = true;
break;
case KeyEvent.VK_DELETE:
if (!CustomSingleLineTextField.this.readonly) {
if (hasSelection()) {
if (e.isShiftDown()) {
cut();
}
else {
deleteSelection();
}
}
else if (CustomSingleLineTextField.this.caretPosition < CustomSingleLineTextField.this.text.length()) {
CustomSingleLineTextField.this.text = CustomSingleLineTextField.this.text.substring(0, CustomSingleLineTextField.this.caretPosition) +
CustomSingleLineTextField.this.text.substring(CustomSingleLineTextField.this.caretPosition + 1);
notifyTextChanged();
CustomSingleLineTextField.this.dirty = true;
}
}
break;
case KeyEvent.VK_C:
if (ctrlPressed) {
copy();
e.consume();
}
break;
case KeyEvent.VK_X:
if (ctrlPressed && !CustomSingleLineTextField.this.readonly) {
cut();
e.consume();
}
break;
case KeyEvent.VK_V:
if (ctrlPressed && !CustomSingleLineTextField.this.readonly) {
paste();
e.consume();
}
break;
case KeyEvent.VK_INSERT:
if (ctrlPressed) {
copy();
e.consume();
}
else if (shiftPressed && !CustomSingleLineTextField.this.readonly) {
paste();
e.consume();
}
break;
case KeyEvent.VK_A:
if (ctrlPressed) {
selectAll();
e.consume();
}
break;
}
}
@Override
public void keyReleased(final KeyEvent e) {
}
});
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
CustomSingleLineTextField.this.requestFocus();
if (!CustomSingleLineTextField.this.enabled) {
e.consume();
return;
}
updateCaretPosition(e.getX());
if (!e.isShiftDown()) {
CustomSingleLineTextField.this.selectionAnchor = CustomSingleLineTextField.this.caretPosition;
}
e.consume();
}
@Override
public void mouseClicked(final MouseEvent e) {
if (!CustomSingleLineTextField.this.enabled) {
e.consume();
return;
}
if (e.getClickCount() == 2) {
selectWordAtCaret();
e.consume();
}
}
@Override
public void mouseReleased(final MouseEvent e) {
e.consume();
}
});
this.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseMoved(final MouseEvent e) {
e.consume();
}
@Override
public void mouseDragged(final MouseEvent e) {
if (!CustomSingleLineTextField.this.enabled) {
e.consume();
return;
}
updateCaretPosition(e.getX());
e.consume();
}
});
this.addFocusListener(new FocusListener() {
@Override
public void focusGained(final FocusEvent e) {
CustomSingleLineTextField.this.dirty = true;
}
@Override
public void focusLost(final FocusEvent e) {
CustomSingleLineTextField.this.dirty = true;
}
});
}
private void insertChar(final char ch) {
this.text = this.text.substring(0, this.caretPosition) + ch + this.text.substring(this.caretPosition);
this.caretPosition++;
this.selectionAnchor = this.caretPosition;
ensureCaretVisible();
notifyTextChanged();
this.dirty = true;
}
private void backspace() {
if (hasSelection()) {
deleteSelection();
}
else if (this.caretPosition > 0) {
this.text = this.text.substring(0, this.caretPosition - 1) + this.text.substring(this.caretPosition);
this.caretPosition--;
this.selectionAnchor = this.caretPosition;
ensureCaretVisible();
notifyTextChanged();
this.dirty = true;
}
}
private void notifyTextChanged() {
if (this.onTextChanged != null) {
this.onTextChanged.accept(this.text);
}
}
private void updateCaretPosition(final int mouseX) {
if (this.getGraphics() == null) {
return;
}
final int logicalMouseX = toLogicalCoord(mouseX);
final int textX = logicalMouseX - TEXT_PADDING + this.scrollOffsetX;
int bestPos = 0;
int bestDist = Integer.MAX_VALUE;
for (int j = 0; j <= this.text.length(); j++) {
final String substr = this.text.substring(0, j);
final int textWidth = G2DDrawFont.getTextDimensions(substr, this.getGraphics(), this.fontSize, 1.0).getX();
final int dist = Math.abs(textWidth - textX);
if (dist < bestDist) {
bestDist = dist;
bestPos = j;
}
}
this.caretPosition = bestPos;
this.dirty = true;
}
private void ensureCaretVisible() {
if (this.getGraphics() == null) {
return;
}
final String beforeCaret = this.text.substring(0, this.caretPosition);
final int caretX = G2DDrawFont.getTextDimensions(beforeCaret, this.getGraphics(), this.fontSize, 1.0).getX();
final int visibleWidth = getVisibleWidth();
if (caretX < this.scrollOffsetX) {
this.scrollOffsetX = Math.max(0, caretX - TEXT_PADDING);
}
else if (caretX > this.scrollOffsetX + visibleWidth - TEXT_PADDING * 2) {
this.scrollOffsetX = caretX - visibleWidth + TEXT_PADDING * 2;
}
this.scrollOffsetX = Math.max(0, this.scrollOffsetX);
this.dirty = true;
}
private int getVisibleWidth() {
return (int) (this.getWidth() / this.scaleFactor);
}
private int toLogicalCoord(final int screenCoord) {
return (int) (screenCoord / this.scaleFactor);
}
private boolean hasSelection() {
return this.caretPosition != this.selectionAnchor;
}
private int getSelectionStart() {
return Math.min(this.caretPosition, this.selectionAnchor);
}
private int getSelectionEnd() {
return Math.max(this.caretPosition, this.selectionAnchor);
}
private String getSelectedText() {
if (!hasSelection()) {
return "";
}
return this.text.substring(getSelectionStart(), getSelectionEnd());
}
private void deleteSelection() {
if (!hasSelection()) {
return;
}
final int start = getSelectionStart();
final int end = getSelectionEnd();
this.text = this.text.substring(0, start) + this.text.substring(end);
this.caretPosition = start;
this.selectionAnchor = start;
notifyTextChanged();
this.dirty = true;
}
private void copy() {
if (!hasSelection()) {
return;
}
final String selectedText = getSelectedText();
final StringSelection selection = new StringSelection(selectedText);
final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(selection, null);
}
private void cut() {
if (!hasSelection() || this.readonly) {
return;
}
copy();
deleteSelection();
}
private void paste() {
if (this.readonly) {
return;
}
try {
final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
String clipboardText = (String) clipboard.getData(DataFlavor.stringFlavor);
if (clipboardText != null && !clipboardText.isEmpty()) {
clipboardText = clipboardText.replace("\r\n", " ").replace('\n', ' ').replace('\r', ' ');
deleteSelection();
this.text = this.text.substring(0, this.caretPosition) + clipboardText + this.text.substring(this.caretPosition);
this.caretPosition += clipboardText.length();
this.selectionAnchor = this.caretPosition;
ensureCaretVisible();
notifyTextChanged();
this.dirty = true;
}
}
}
catch (final Exception ex) {
}
}
private void selectAll() {
this.selectionAnchor = 0;
this.caretPosition = this.text.length();
this.dirty = true;
}
private void selectWordAtCaret() {
if (this.text.isEmpty()) {
return;
}
int start = this.caretPosition;
int end = this.caretPosition;
while (start > 0 && isWordCharacter(this.text.charAt(start - 1))) {
start--;
}
while (end < this.text.length() && isWordCharacter(this.text.charAt(end))) {
end++;
}
if (start < end) {
this.selectionAnchor = start;
this.caretPosition = end;
this.dirty = true;
}
}
private boolean isWordCharacter(final char ch) {
return Character.isLetterOrDigit(ch) || ch == '_';
}
@Override
public void update(final Graphics g) {
}
@Override
public void paint(final Graphics g) {
if (this.themeManager == null) {
return;
}
final var g2d = (Graphics2D) g;
final Theme theme = this.themeManager.getCurrentTheme();
final Color bgColor = this.backgroundColor != null ? this.backgroundColor : theme.getBackgroundNormal().toColor();
g2d.setColor(bgColor);
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
if (this.cachedRawFontHeight == 0) {
this.cachedRawFontHeight = G2DDrawFont.getTextDimensions("ÄÖÜABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzgjpqy", g2d, this.fontSize, 1.0).getY();
}
final int rawFontHeight = this.cachedRawFontHeight > 0 ? this.cachedRawFontHeight : this.fontSize;
final int textY = (int) ((this.getHeight() / this.scaleFactor - rawFontHeight) / 2.0);
final Color fgColor = this.foregroundColor != null ? this.foregroundColor : theme.getTextNormal().toColor();
final var oldClip = g2d.getClip();
g2d.setClip(0, 0, this.getWidth(), this.getHeight());
final boolean hasSelection = hasSelection();
final int selStart = hasSelection ? getSelectionStart() : 0;
final int selEnd = hasSelection ? getSelectionEnd() : 0;
if (hasSelection && !this.text.isEmpty()) {
final String beforeSel = this.text.substring(0, selStart);
final String selected = this.text.substring(selStart, selEnd);
final String afterSel = this.text.substring(selEnd);
final int beforeSelWidth = G2DDrawFont.getTextDimensions(beforeSel, g2d, this.fontSize, this.scaleFactor).getX();
final int selWidth = G2DDrawFont.getTextDimensions(selected, g2d, this.fontSize, this.scaleFactor).getX();
final int drawOffsetX = TEXT_PADDING - (int) (this.scrollOffsetX * this.scaleFactor);
if (!beforeSel.isEmpty()) {
g2d.setColor(fgColor);
G2DDrawFont.renderText(g2d, null, this.scaleFactor, (int) (TEXT_PADDING / this.scaleFactor) - this.scrollOffsetX, textY, this.fontSize, beforeSel);
}
g2d.setColor(theme.getSelectionPrimary().toColor());
g2d.fillRect(drawOffsetX + beforeSelWidth, (int) (textY * this.scaleFactor), selWidth, (int) (rawFontHeight * this.scaleFactor));
g2d.setColor(theme.getBackgroundNormal().toColor());
G2DDrawFont.renderText(g2d, null, this.scaleFactor,
(int) ((drawOffsetX + beforeSelWidth) / this.scaleFactor), textY, this.fontSize, selected);
if (!afterSel.isEmpty()) {
g2d.setColor(fgColor);
G2DDrawFont.renderText(g2d, null, this.scaleFactor,
(int) ((drawOffsetX + beforeSelWidth + selWidth) / this.scaleFactor), textY, this.fontSize, afterSel);
}
}
else {
final var textDim = G2DDrawFont.getTextDimensions(this.text, g2d, this.fontSize, 1.0);
final var textDimScaled = ImmutableCoordI2.create((int) (textDim.getX() * this.scaleFactor), (int) (textDim.getY() * this.scaleFactor));
g2d.setColor(fgColor);
G2DDrawFont.renderText(g2d, textDimScaled, this.scaleFactor, TEXT_PADDING - this.scrollOffsetX, textY, this.fontSize, this.text);
}
if (this.isFocusOwner() && this.enabled) {
final long currentTime = System.currentTimeMillis();
if (currentTime - this.lastBlinkTime > CARET_BLINK_INTERVAL) {
this.caretVisible = !this.caretVisible;
this.lastBlinkTime = currentTime;
}
if (this.caretVisible) {
final String beforeCaret = this.text.substring(0, this.caretPosition);
final var textDim = beforeCaret.isEmpty()
? ImmutableCoordI2.create(0, 0)
: G2DDrawFont.getTextDimensions(beforeCaret, g2d, this.fontSize, this.scaleFactor);
final int caretX = textDim.getX() + TEXT_PADDING - (int) (this.scrollOffsetX * this.scaleFactor);
final int caretY = (int) (textY * this.scaleFactor);
final int caretHeight = (int) (rawFontHeight * this.scaleFactor);
g2d.setColor((currentTime % 1000 < 500) ? theme.getSelectionPrimary().toColor() : theme.getSelectionSecondary().toColor());
g2d.fillRect(caretX, caretY, 2, caretHeight);
}
}
g2d.setClip(oldClip);
}
}

View File

@@ -0,0 +1,154 @@
package de.extio.game_engine.renderer.g2d.control.impl;
import de.extio.game_engine.renderer.g2d.G2DRenderer;
import de.extio.game_engine.renderer.g2d.control.components.CustomSingleLineTextField;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.BaseControl;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.TextfieldControl;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.TextfieldData;
import de.extio.game_engine.renderer.model.color.RgbaColor;
import de.extio.game_engine.renderer.model.event.UiControlEvent;
public class G2DSingleLineTextfieldControlImpl extends G2DBaseControlImpl implements TextfieldControl {
private CustomSingleLineTextField textField;
private boolean readonly;
private long lastCaptionUpdateTime;
private long lastCaptionUpdateTimeInternal;
private String lastText = "";
private RgbaColor backgroundColor;
@Override
public void setCustomData(final TextfieldData data) {
if (data != null) {
this.setReadonly(data.readonly());
this.setBackgroundColor(data.backgroundColor());
}
}
public G2DSingleLineTextfieldControlImpl() {
}
private void setBackgroundColor(final RgbaColor color) {
this.modified |= (color == null) ? this.backgroundColor != null : !color.equals(this.backgroundColor);
this.backgroundColor = color;
}
private void setReadonly(final boolean readonly) {
this.modified |= readonly != this.readonly;
this.readonly = readonly;
}
@Override
public BaseControl setCaption(final String caption) {
this.caption = caption;
return this;
}
public void setLastCaptionUpdateTime(final long lastCaptionUpdateTime) {
this.modified |= this.lastCaptionUpdateTime != lastCaptionUpdateTime;
this.lastCaptionUpdateTime = lastCaptionUpdateTime;
}
@Override
public void build() {
super.build();
this.textField = new CustomSingleLineTextField(
this.rendererData.getThemeManager(),
text -> {
this.lastText = text;
this.lastCaptionUpdateTimeInternal = System.currentTimeMillis();
try {
Thread.sleep(2);
}
catch (final InterruptedException e) {
}
this.rendererData.getEventService().fire(new UiControlEvent(this.id, text));
},
() -> {
final String submitText = (this.lastText == null ? "" : this.lastText) + "\n";
this.textField.setText("");
this.lastText = "";
this.caption = null;
this.lastCaptionUpdateTimeInternal = System.currentTimeMillis();
this.rendererData.getEventService().fire(new UiControlEvent(this.id, submitText));
});
this.textField.setName(this.id);
this.textField.setFocusable(true);
this.initControl();
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
mainFrame.add(this.textField);
this.updateAllComponentZOrder();
}
@Override
public void performAction() {
}
@Override
public void render() {
if (this.lastCaptionUpdateTime > this.lastCaptionUpdateTimeInternal && this.caption != null && !this.caption.equals(this.lastText)) {
this.textField.setText(this.caption);
this.lastText = this.caption;
this.lastCaptionUpdateTimeInternal = this.lastCaptionUpdateTime;
}
final boolean componentDirty = this.textField.isDirty();
if (this.modified || componentDirty) {
this.rebuildBufferedImage();
this.bufferedImageGraphics.setClip(0, 0, this.bufferedImage.getWidth(), this.bufferedImage.getHeight());
this.textField.setSize(this.bufferedImage.getWidth(), this.bufferedImage.getHeight());
if (this.modified || componentDirty) {
this.initControl();
}
this.textField.paint(this.bufferedImageGraphics);
this.textField.setDirty(false);
this.updateAllComponentZOrder();
}
else if (this.positionModified) {
this.textField.setLocation(this.x, this.y);
this.positionModified = false;
this.updateAllComponentZOrder();
}
this.textField.paint(this.bufferedImageGraphics);
super.render();
}
@Override
public void close() {
if (this.textField.isFocusOwner()) {
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().requestFocus();
}
this.textField.setVisible(false);
this.textField.invalidate();
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().remove(this.textField);
super.close();
}
private void initControl() {
final var theme = this.rendererData.getThemeManager().getCurrentTheme();
final var bgColor = this.backgroundColor != null ? this.backgroundColor.toAwtColor() : theme.getBackgroundNormal().toColor();
final var fgColor = theme.getTextNormal().toColor();
this.textField.setBackgroundColor(bgColor);
this.textField.setForegroundColor(fgColor);
this.textField.setFontSize(this.fontSize);
this.textField.setScaleFactor(this.scaleFactor);
this.textField.setLocation(this.x, this.y);
this.textField.setSize(this.width, this.height);
this.positionModified = false;
this.textField.setVisible(this.visible);
this.textField.setEnabled(this.enabled);
this.textField.setReadonly(this.readonly);
}
}