Newer
Older
AlgebraicDataflowArchitectureModel / GameEngine / src / main / java / gameEngine / views / InputField.java
package gameEngine.views;

import gameEngine.Time;
import gameEngine.input.MouseInput;
import org.joml.Vector3f;
import gameEngine.input.Input;
import static org.lwjgl.glfw.GLFW.*;

public class InputField implements IUpdatable {

    private Text displayText;
    private boolean isFocused;
    private Vector3f position;
    private float width, height;
    private String placeholder;
    private String currentText;   // 入力中のテキスト
    private String displayedText; // 表示するテキスト
    private Sprite backgroundSprite;
    private int cursorPosition;  // カーソル位置
    private boolean showCursor;  // カーソルの表示フラグ
    private float cursorBlinkTimer;  // カーソルの点滅タイマー
    private final float CURSOR_BLINK_INTERVAL = 0.5f;  // カーソルの点滅間隔(秒)
    private Color defaultColor;
    private OnChangeListener onChangeListener;

    public interface OnChangeListener {
        void onChange(String newValue);
    }

    public InputField(float posX, float posY, float width, float height, String placeholder, int textSize) {
        this.position = new Vector3f(posX, posY, 0);
        this.width = width;
        this.height = height;
        this.placeholder = placeholder;
        this.currentText = "";
        this.displayedText = "";
        this.isFocused = false;
        this.cursorPosition = 0;
        this.showCursor = true;
        this.cursorBlinkTimer = 0.0f;

        this.backgroundSprite = new Sprite(Window.resourcePath + "EditorFrame.png", posX, posY, width, height);
        this.backgroundSprite.setSize(width, height);

        displayText = new Text(posX + 2, posY, placeholder, textSize);
        displayText.setText(displayedText.isEmpty() ? placeholder : displayedText);
        displayText.setColor(new Color(1,1,1,1)); // RGB値 (白)
    }

    public void setOnChangeListener(OnChangeListener listener) {
        this.onChangeListener = listener;
    }

    public void update() {
        handleInput();
        updateCursorBlink(Time.deltaTime);
        render();
    }

    private void handleInput() {
        float mouseX = MouseInput.getX();
        float mouseY = MouseInput.getY();

        if (!isFocused && !currentText.equals(displayedText)) {
            currentText = displayedText;
        }

        if (Input.GetMouseButtonDown(GLFW_MOUSE_BUTTON_LEFT)) {
            boolean wasFocused = isFocused;
            isFocused = isMouseOver(mouseX, mouseY);

            if (!isFocused && wasFocused && onChangeListener != null) {
                displayedText = currentText; // 最終的な入力内容を更新
                onChangeListener.onChange(currentText);
            }
        }

        if (isFocused) {
            handleTextInput();

            if (Input.GetKeyDown(GLFW_KEY_ENTER)) {
                // Enterキーでフォーカス解除&内容を確定
                isFocused = false;
                displayedText = currentText; // 入力内容を確定
                if (onChangeListener != null) {
                    onChangeListener.onChange(currentText);
                }
            }

            if (Input.GetKeyDown(GLFW_KEY_BACKSPACE) && cursorPosition > 0) {
                currentText = currentText.substring(0, cursorPosition - 1) + currentText.substring(cursorPosition);
                cursorPosition--;
            }
        }
    }

    private void handleTextInput() {
        boolean isShiftPressed = Input.GetKey(GLFW_KEY_LEFT_SHIFT) || Input.GetKey(GLFW_KEY_RIGHT_SHIFT);

        // 英字入力
        for (int key = GLFW_KEY_A; key <= GLFW_KEY_Z; key++) {
            if (Input.GetKeyDown(key)) {
                char typedChar = (char) (isShiftPressed ? key : key + 32); // シフトキーで大文字・小文字切り替え
                currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition);
                cursorPosition++;
            }
        }

        // 数字入力 (メインの数字キー 0-9)
        for (int key = GLFW_KEY_0; key <= GLFW_KEY_9; key++) {
            if (Input.GetKeyDown(key)) {
                char typedChar = (char) ('0' + (key - GLFW_KEY_0));
                currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition);
                cursorPosition++;
                displayText.setText(currentText.isEmpty() ? placeholder : currentText);
            }
        }

        // テンキーの数字入力 (0-9)
        for (int key = GLFW_KEY_KP_0; key <= GLFW_KEY_KP_9; key++) {
            if (Input.GetKeyDown(key)) {
                char typedChar = (char) ('0' + (key - GLFW_KEY_KP_0));
                currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition);
                cursorPosition++;
                displayText.setText(currentText.isEmpty() ? placeholder : currentText);
            }
        }

        // 記号入力(上手くいったもののみ)
        if (isShiftPressed) {
            if (Input.GetKeyDown(GLFW_KEY_MINUS)) appendChar('=');
            else if (Input.GetKeyDown(GLFW_KEY_SEMICOLON)) appendChar('+');
            else if (Input.GetKeyDown(GLFW_KEY_COMMA)) appendChar('<');
            else if (Input.GetKeyDown(GLFW_KEY_PERIOD)) appendChar('>');
            else if (Input.GetKeyDown(GLFW_KEY_SLASH)) appendChar('?');

        } else {
            if (Input.GetKeyDown(GLFW_KEY_MINUS)) appendChar('-');
            else if (Input.GetKeyDown(GLFW_KEY_SEMICOLON)) appendChar(';');
            else if (Input.GetKeyDown(GLFW_KEY_COMMA)) appendChar(',');
            else if (Input.GetKeyDown(GLFW_KEY_PERIOD)) appendChar('.');
            else if (Input.GetKeyDown(GLFW_KEY_SLASH)) appendChar('/');
        }


        // Deleteキーでカーソル右側の文字を削除
        if (Input.GetKeyDown(GLFW_KEY_DELETE) && cursorPosition < currentText.length()) {
            currentText = currentText.substring(0, cursorPosition) + currentText.substring(cursorPosition + 1);
            displayText.setText(currentText.isEmpty() ? placeholder : currentText);
        }

        // 左右キーでカーソル移動
        if (Input.GetKeyDown(GLFW_KEY_LEFT) && cursorPosition > 0) {
            cursorPosition--;
        }
        if (Input.GetKeyDown(GLFW_KEY_RIGHT) && cursorPosition < currentText.length()) {
            cursorPosition++;
        }
    }

    private void appendChar(char c) {
        currentText = currentText.substring(0, cursorPosition) + c + currentText.substring(cursorPosition);
        cursorPosition++;
        displayText.setText(currentText.isEmpty() ? placeholder : currentText);
    }

    private void updateCursorBlink(float deltaTime) {
        cursorBlinkTimer += deltaTime;
        if (cursorBlinkTimer > CURSOR_BLINK_INTERVAL) {
            cursorBlinkTimer = 0.0f;
            showCursor = !showCursor;
        }
    }

    private void setCursorPositionAtMouse(float mouseX) {
        float relativeMouseX = mouseX - position.x;

        if (relativeMouseX <= 0) {
            cursorPosition = 0;
        } else if (relativeMouseX >= displayText.getDisplayedWidth()) {
            cursorPosition = currentText.length();
        } else {
            float cumulativeWidth = 0;
            for (int i = 0; i < currentText.length(); i++) {
                cumulativeWidth += displayText.getWidthAtIndex(i); // 各文字の幅を取得
                if (cumulativeWidth >= relativeMouseX) {
                    cursorPosition = i; // 最も近い文字位置
                    break;
                }
            }
        }
    }

    public boolean isMouseOver(float mouseX, float mouseY) {
        return mouseX >= position.x && mouseX <= position.x + width &&
                mouseY >= position.y && mouseY <= position.y + height;
    }

    public void render() {
        backgroundSprite.update();
        displayText.setText(isFocused ? currentText : (displayedText.isEmpty() ? placeholder : displayedText));
        displayText.update();

        if (isFocused && showCursor) {
            float cursorX = position.x + displayText.getWidthAtIndex(cursorPosition);
            Text cursor = new Text(cursorX, position.y, "|", displayText.textSize);
            cursor.setColor(new Color(1,1,1,1)); // RGB値 (白)
            cursor.update();
        }
    }

    public void copyDisplayedTextToCurrent(){
        currentText = displayedText;
    }

    public void setText(String text) {
        this.displayedText = text;
        if (!isFocused) {
            displayText.setText(displayedText.isEmpty() ? placeholder : displayedText);
        }
    }
}