public interface GuiRenderer {

    ReadablePoint getOpenGlOffset();

    MatrixStack getMatrixStack();

    ReadableDimension getSize();

    void setDrawingArea(int x, int y, int width, int height);

    void bindTexture(Identifier location);

    void bindTexture(int glId);

    void drawTexturedRect(int x, int y, int u, int v, int width, int height);

    void drawTexturedRect(int x, int y, int u, int v, int width, int height, int uWidth, int vHeight, int textureWidth, int textureHeight);

    void drawRect(int x, int y, int width, int height, int color);

    void drawRect(int x, int y, int width, int height, ReadableColor color);

    void drawRect(int x, int y, int width, int height, int topLeftColor, int topRightColor, int bottomLeftColor, int bottomRightColor);

    void drawRect(int x, int y, int width, int height, ReadableColor topLeftColor, ReadableColor topRightColor, ReadableColor bottomLeftColor, ReadableColor bottomRightColor);

    int drawString(int x, int y, int color, String text);

    int drawString(int x, int y, ReadableColor color, String text);

    int drawCenteredString(int x, int y, int color, String text);

    int drawCenteredString(int x, int y, ReadableColor color, String text);

    int drawString(int x, int y, int color, String text, boolean shadow);

    int drawString(int x, int y, ReadableColor color, String text, boolean shadow);

    int drawCenteredString(int x, int y, int color, String text, boolean shadow);

    int drawCenteredString(int x, int y, ReadableColor color, String text, boolean shadow);

    /**
     * Inverts all colors on the screen.
     * @param right Right border of the inverted rectangle
     * @param bottom Bottom border of the inverted rectangle
     * @param left Left border of the inverted rectangle
     * @param top Top border of the inverted rectangle
     */
    void invertColors(int right, int bottom, int left, int top);

} public class RenderInfo {
    public final float partialTick;
    public final int mouseX;
    public final int mouseY;
    public final int layer;

    public RenderInfo(float partialTick, int mouseX, int mouseY, int layer) {
        this.partialTick = partialTick;
        this.mouseX = mouseX;
        this.mouseY = mouseY;
        this.layer = layer;
    }

    public RenderInfo offsetMouse(int minusX, int minusY) {
        return new RenderInfo(partialTick, mouseX - minusX, mouseY - minusY, layer);
    }

    public RenderInfo layer(int layer) {
        return this.layer == layer ? this : new RenderInfo(partialTick, mouseX, mouseY, layer);
    }

    public void addTo(CrashReport crashReport) {
        CrashReportSection category = crashReport.addElement("Render info details");
        MCVer.addDetail(category, "Partial Tick", () -> "" + partialTick);
        MCVer.addDetail(category, "Mouse X", () -> "" + mouseX);
        MCVer.addDetail(category, "Mouse Y", () -> "" + mouseY);
        MCVer.addDetail(category, "Layer", () -> "" + layer);
    }

    public float getPartialTick() {
        return this.partialTick;
    }

    public int getMouseX() {
        return this.mouseX;
    }

    public int getMouseY() {
        return this.mouseY;
    }

    public int getLayer() {
        return this.layer;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RenderInfo that = (RenderInfo) o;
        return Float.compare(that.partialTick, partialTick) == 0 &&
                mouseX == that.mouseX &&
                mouseY == that.mouseY &&
                layer == that.layer;
    }

    @Override
    public int hashCode() {
        return Objects.hash(partialTick, mouseX, mouseY, layer);
    }

    @Override
    public String toString() {
        return "RenderInfo{" +
                "partialTick=" + partialTick +
                ", mouseX=" + mouseX +
                ", mouseY=" + mouseY +
                ", layer=" + layer +
                '}';
    }
} public interface GuiContainer<T extends GuiContainer<T>> extends ComposedGuiElement<T> {

    T setLayout(Layout layout);
    Layout getLayout();

    void convertFor(GuiElement element, Point point);

    /**
     * Converts the global coordinates of the point to ones relative to the element.
     * @param element The element, must be part of this container
     * @param point The point
     * @param relativeLayer Layer at which the point is relative to this element,
     *                      positive values are above this element
     */
    void convertFor(GuiElement element, Point point, int relativeLayer);

    Map<GuiElement, LayoutData> getElements();
    T addElements(LayoutData layoutData, GuiElement... elements);
    T removeElement(GuiElement element);
    T sortElements();
    T sortElements(Comparator<GuiElement> comparator);

    ReadableColor getBackgroundColor();
    T setBackgroundColor(ReadableColor backgroundColor);
} public abstract class AbstractGuiElement<T extends AbstractGuiElement<T>> implements GuiElement<T> {
    protected static final Identifier TEXTURE = new Identifier("jgui", "gui.png");


    private final MinecraftClient minecraft = MCVer.getMinecraft();

    private GuiContainer container;

    private GuiElement tooltip;

    private boolean enabled = true;

    protected Dimension minSize, maxSize;

    /**
     * The last size this element was render at layer 0.
     * May be {@code null} when this element has not yet been rendered.
     */
    private ReadableDimension lastSize;

    public AbstractGuiElement() {
    }

    public AbstractGuiElement(GuiContainer container) {
        container.addElements(null, this);
    }

    protected abstract T getThis();

    @Override
    public void layout(ReadableDimension size, RenderInfo renderInfo) {
        if (size == null) {
            if (getContainer() == null) {
                throw new RuntimeException("Any top containers must implement layout(null, ...) themselves!");
            }
            getContainer().layout(size, renderInfo.layer(renderInfo.layer + getLayer()));
            return;
        }
        if (renderInfo.layer == 0) {
            lastSize = size;
        }
    }

    @Override
    public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
    }

    @Override
    public T setEnabled(boolean enabled) {
        this.enabled = enabled;
        return getThis();
    }

    @Override
    public T setEnabled() {
        return setEnabled(true);
    }

    @Override
    public T setDisabled() {
        return setEnabled(false);
    }

    @Override
    public GuiElement getTooltip(RenderInfo renderInfo) {
        if (tooltip != null && lastSize != null) {
            Point mouse = new Point(renderInfo.mouseX, renderInfo.mouseY);
            if (container != null) {
                container.convertFor(this, mouse);
            }
            if (mouse.getX() > 0
                    && mouse.getY() > 0
                    && mouse.getX() < lastSize.getWidth()
                    && mouse.getY() < lastSize.getHeight()) {
                return tooltip;
            }
        }
        return null;
    }

    @Override
    public T setTooltip(GuiElement tooltip) {
        this.tooltip = tooltip;
        return getThis();
    }

    @Override
    public T setContainer(GuiContainer container) {
        this.container = container;
        return getThis();
    }

    public T setMinSize(ReadableDimension minSize) {
        this.minSize = new Dimension(minSize);
        return getThis();
    }

    public T setMaxSize(ReadableDimension maxSize) {
        this.maxSize = new Dimension(maxSize);
        return getThis();
    }

    public T setSize(ReadableDimension size) {
        setMinSize(size);
        return setMaxSize(size);
    }

    public T setSize(int width, int height) {
        return setSize(new Dimension(width, height));
    }

    public T setWidth(int width) {
        if (minSize == null) {
            minSize = new Dimension(width, 0);
        } else {
            minSize.setWidth(width);
        }
        if (maxSize == null) {
            maxSize = new Dimension(width, Integer.MAX_VALUE);
        } else {
            maxSize.setWidth(width);
        }
        return getThis();
    }

    public T setHeight(int height) {
        if (minSize == null) {
            minSize = new Dimension(0, height);
        } else {
            minSize.setHeight(height);
        }
        if (maxSize == null) {
            maxSize = new Dimension(Integer.MAX_VALUE, height);
        } else {
            maxSize.setHeight(height);
        }
        return getThis();
    }

    public int getLayer() {
        return 0;
    }

    @Override
    public ReadableDimension getMinSize() {
        ReadableDimension calcSize = calcMinSize();
        if (minSize == null) {
            return calcSize;
        } else {
            if (minSize.getWidth() >= calcSize.getWidth() && minSize.getHeight() >= calcSize.getHeight()) {
                return minSize;
            } else {
                return new Dimension(
                        Math.max(calcSize.getWidth(), minSize.getWidth()),
                        Math.max(calcSize.getHeight(), minSize.getHeight())
                );
            }
        }
    }

    protected abstract ReadableDimension calcMinSize();

    @Override
    public ReadableDimension getMaxSize() {
        return maxSize == null ? new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE) : maxSize;
    }

    public MinecraftClient getMinecraft() {
        return this.minecraft;
    }

    public GuiContainer getContainer() {
        return this.container;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    protected ReadableDimension getLastSize() {
        return this.lastSize;
    }
} public interface Clickable {
    boolean mouseClick(ReadablePoint position, int button);
} public interface Focusable<T extends Focusable<T>> {

    boolean isFocused();
    T setFocused(boolean focused);

    T onFocusChange(Consumer<Boolean> consumer);

    Focusable getNext();
    T setNext(Focusable next);

    Focusable getPrevious();
    T setPrevious(Focusable previous);

} public interface Tickable {
    void tick();
} public interface Typeable {
    boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown);
} public interface Consumer<T> {
    void consume(T obj);
} public class MCVer {
    public static MinecraftClient getMinecraft() {
        return MinecraftClient.getInstance();
    }

    private static class ScissorBounds {
        private static final ScissorBounds DISABLED = new ScissorBounds(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
        private final int x;
        private final int y;
        private final int width;
        private final int height;

        private ScissorBounds(int x, int y, int width, int height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ScissorBounds that = (ScissorBounds) o;
            return x == that.x && y == that.y && width == that.width && height == that.height;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y, width, height);
        }
    }
    private static final ArrayDeque<ScissorBounds> scissorStateStack = new ArrayDeque<>();
    private static ScissorBounds scissorState = ScissorBounds.DISABLED;

    public static void pushScissorState() {
        scissorStateStack.push(scissorState);
    }

    public static void popScissorState() {
        setScissorBounds(scissorStateStack.pop());
    }

    public static void setScissorBounds(int x, int y, int width, int height) {
        setScissorBounds(new ScissorBounds(x, y, width, height));
    }

    public static void setScissorDisabled() {
        setScissorBounds(ScissorBounds.DISABLED);
    }

    private static void setScissorBounds(ScissorBounds newState) {
        ScissorBounds oldState = MCVer.scissorState;
        if (Objects.equals(oldState, newState)) {
            return;
        }

        scissorState = newState;

        boolean isEnabled = newState != ScissorBounds.DISABLED;
        boolean wasEnabled = oldState != ScissorBounds.DISABLED;

        if (isEnabled) {
            if (!wasEnabled) {
                GL11.glEnable(GL11.GL_SCISSOR_TEST);
            }
            GL11.glScissor(scissorState.x, scissorState.y, scissorState.width, scissorState.height);
        } else {
            GL11.glDisable(GL11.GL_SCISSOR_TEST);
        }
    }

    //#if MC>=11400
    public static Window newScaledResolution(MinecraftClient mc) {
        //#if MC>=11500
        return mc.getWindow();
        //#else
        //$$ return mc.window;
        //#endif
    }
    //#else
    //$$ public static ScaledResolution newScaledResolution(Minecraft mc) {
        //#if MC>=10809
        //$$ return new ScaledResolution(mc);
        //#else
        //$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
        //#endif
    //$$ }
    //#endif

    public static void addDetail(CrashReportSection category, String name, Callable<String> callable) {
        //#if MC>=10904
        //#if MC>=11200
        category.add(name, callable::call);
        //#else
        //$$ category.setDetail(name, callable::call);
        //#endif
        //#else
        //$$ category.addCrashSectionCallable(name, callable);
        //#endif
    }

    public static void drawRect(int right, int bottom, int left, int top) {
        //#if MC>=10800
        Tessellator tessellator = Tessellator.getInstance();
        //#else
        //$$ Tessellator tessellator = Tessellator.instance;
        //#endif
        //#if MC>=10904
        BufferBuilder vertexBuffer = tessellator.getBuffer();
        //#else
        //#if MC>=10800
        //$$ WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
        //#else
        //$$ Tessellator vertexBuffer = tessellator;
        //#endif
        //#endif
        //#if MC>=10809
        //#if MC>=11700
        //$$ vertexBuffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION);
        //#else
        vertexBuffer.begin(GL11.GL_QUADS, VertexFormats.POSITION);
        //#endif
        vertexBuffer.vertex(right, top, 0).next();
        vertexBuffer.vertex(left, top, 0).next();
        vertexBuffer.vertex(left, bottom, 0).next();
        vertexBuffer.vertex(right, bottom, 0).next();
        //#else
        //$$ vertexBuffer.startDrawingQuads();
        //$$ vertexBuffer.addVertex(right, top, 0);
        //$$ vertexBuffer.addVertex(left, top, 0);
        //$$ vertexBuffer.addVertex(left, bottom, 0);
        //$$ vertexBuffer.addVertex(right, bottom, 0);
        //#endif
        tessellator.draw();
    }

    public static void drawRect(int x, int y, int width, int height, ReadableColor tl, ReadableColor tr, ReadableColor bl, ReadableColor br) {
        //#if MC>=10800
        Tessellator tessellator = Tessellator.getInstance();
        BufferBuilder vertexBuffer = tessellator.getBuffer();
        //#else
        //$$ Tessellator tessellator = Tessellator.instance;
        //$$ Tessellator vertexBuffer = tessellator;
        //#endif
        //#if MC>=10809
        //#if MC>=11700
        //$$ vertexBuffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        //#else
        vertexBuffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
        //#endif
        vertexBuffer.vertex(x, y + height, 0).color(bl.getRed(), bl.getGreen(), bl.getBlue(), bl.getAlpha()).next();
        vertexBuffer.vertex(x + width, y + height, 0).color(br.getRed(), br.getGreen(), br.getBlue(), br.getAlpha()).next();
        vertexBuffer.vertex(x + width, y, 0).color(tr.getRed(), tr.getGreen(), tr.getBlue(), tr.getAlpha()).next();
        vertexBuffer.vertex(x, y, 0).color(tl.getRed(), tl.getGreen(), tl.getBlue(), tl.getAlpha()).next();
        //#else
        //$$ vertexBuffer.startDrawingQuads();
        //$$ vertexBuffer.setColorRGBA(bl.getRed(), bl.getGreen(), bl.getBlue(), bl.getAlpha());
        //$$ vertexBuffer.addVertex(x, y + height, 0);
        //$$ vertexBuffer.setColorRGBA(br.getRed(), br.getGreen(), br.getBlue(), br.getAlpha());
        //$$ vertexBuffer.addVertex(x + width, y + height, 0);
        //$$ vertexBuffer.setColorRGBA(tr.getRed(), tr.getGreen(), tr.getBlue(), tr.getAlpha());
        //$$ vertexBuffer.addVertex(x + width, y, 0);
        //$$ vertexBuffer.setColorRGBA(tl.getRed(), tl.getGreen(), tl.getBlue(), tl.getAlpha());
        //$$ vertexBuffer.addVertex(x, y, 0);
        //#endif
        tessellator.draw();
    }

    public static TextRenderer getFontRenderer() {
        return getMinecraft().textRenderer;
    }

    //#if FABRIC<=0
    //$$ public static RenderGameOverlayEvent.ElementType getType(RenderGameOverlayEvent event) {
        //#if MC>=10904
        //$$ return event.getType();
        //#else
        //$$ return event.type;
        //#endif
    //$$ }
    //$$
    //$$ public static int getMouseX(GuiScreenEvent.DrawScreenEvent.Post event) {
        //#if MC>=10904
        //$$ return event.getMouseX();
        //#else
        //$$ return event.mouseX;
        //#endif
    //$$ }
    //$$
    //$$ public static int getMouseY(GuiScreenEvent.DrawScreenEvent.Post event) {
        //#if MC>=10904
        //$$ return event.getMouseY();
        //#else
        //$$ return event.mouseY;
        //#endif
    //$$ }
    //#endif

    public static void setClipboardString(String text) {
        //#if MC>=11400
        getMinecraft().keyboard.setClipboard(text);
        //#else
        //$$ GuiScreen.setClipboardString(text);
        //#endif
    }

    public static String getClipboardString() {
        //#if MC>=11400
        return getMinecraft().keyboard.getClipboard();
        //#else
        //$$ return GuiScreen.getClipboardString();
        //#endif
    }

    //#if MC<=10710
    //$$ public static void color(float r, float g, float b) { GL11.glColor3f(r, g, b); }
    //$$ public static void color(float r, float g, float b, float a) { GL11.glColor4f(r, g, b, a); }
    //$$ public static void enableBlend() { GL11.glEnable(GL_BLEND); }
    //$$ public static void enableTexture2D() { GL11.glEnable(GL_TEXTURE_2D); }
    //$$ public static void enableAlpha() { GL11.glEnable(GL_ALPHA_TEST); }
    //$$ public static void disableTexture2D() { GL11.glDisable(GL_TEXTURE_2D); }
    //$$ public static void disableAlpha() { GL11.glDisable(GL_ALPHA_TEST); }
    //$$ public static void blendFunc(int s, int d) { GL11.glBlendFunc(s, d); }
    //$$ public static void tryBlendFuncSeparate(int l, int r, int vl, int vr) { OpenGlHelper.glBlendFunc(l, r, vl, vr); }
    //$$ public static void shadeModel(int mode) { GL11.glShadeModel(mode); }
    //$$ public static void enableColorLogic() { GL11.glEnable(GL_COLOR_LOGIC_OP); }
    //$$ public static void disableColorLogic() { GL11.glDisable(GL_COLOR_LOGIC_OP); }
    //$$ public static void colorLogicOp(int op) { GL11.glLogicOp(op); }
    //#endif

    //#if MC>=11400
    public static abstract class Keyboard {
        public static final int KEY_ESCAPE = GLFW.GLFW_KEY_ESCAPE;
        public static final int KEY_HOME = GLFW.GLFW_KEY_HOME;
        public static final int KEY_END = GLFW.GLFW_KEY_END;
        public static final int KEY_UP = GLFW.GLFW_KEY_UP;
        public static final int KEY_DOWN = GLFW.GLFW_KEY_DOWN;
        public static final int KEY_LEFT = GLFW.GLFW_KEY_LEFT;
        public static final int KEY_RIGHT = GLFW.GLFW_KEY_RIGHT;
        public static final int KEY_BACK = GLFW.GLFW_KEY_BACKSPACE;
        public static final int KEY_DELETE = GLFW.GLFW_KEY_DELETE;
        public static final int KEY_RETURN = GLFW.GLFW_KEY_ENTER;
        public static final int KEY_TAB = GLFW.GLFW_KEY_TAB;
        public static final int KEY_A = GLFW.GLFW_KEY_A;
        public static final int KEY_C = GLFW.GLFW_KEY_C;
        public static final int KEY_V = GLFW.GLFW_KEY_V;
        public static final int KEY_X = GLFW.GLFW_KEY_X;

        public static void enableRepeatEvents(boolean enabled) {
            getMinecraft().keyboard.setRepeatEvents(enabled);
        }
    }
    //#endif
} public static int clamp(int val, int min, int max) {
    return val < min ? min : val > max ? max : val;
} public class MCVer {
    public static MinecraftClient getMinecraft() {
        return MinecraftClient.getInstance();
    }

    private static class ScissorBounds {
        private static final ScissorBounds DISABLED = new ScissorBounds(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
        private final int x;
        private final int y;
        private final int width;
        private final int height;

        private ScissorBounds(int x, int y, int width, int height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ScissorBounds that = (ScissorBounds) o;
            return x == that.x && y == that.y && width == that.width && height == that.height;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y, width, height);
        }
    }
    private static final ArrayDeque<ScissorBounds> scissorStateStack = new ArrayDeque<>();
    private static ScissorBounds scissorState = ScissorBounds.DISABLED;

    public static void pushScissorState() {
        scissorStateStack.push(scissorState);
    }

    public static void popScissorState() {
        setScissorBounds(scissorStateStack.pop());
    }

    public static void setScissorBounds(int x, int y, int width, int height) {
        setScissorBounds(new ScissorBounds(x, y, width, height));
    }

    public static void setScissorDisabled() {
        setScissorBounds(ScissorBounds.DISABLED);
    }

    private static void setScissorBounds(ScissorBounds newState) {
        ScissorBounds oldState = MCVer.scissorState;
        if (Objects.equals(oldState, newState)) {
            return;
        }

        scissorState = newState;

        boolean isEnabled = newState != ScissorBounds.DISABLED;
        boolean wasEnabled = oldState != ScissorBounds.DISABLED;

        if (isEnabled) {
            if (!wasEnabled) {
                GL11.glEnable(GL11.GL_SCISSOR_TEST);
            }
            GL11.glScissor(scissorState.x, scissorState.y, scissorState.width, scissorState.height);
        } else {
            GL11.glDisable(GL11.GL_SCISSOR_TEST);
        }
    }

    //#if MC>=11400
    public static Window newScaledResolution(MinecraftClient mc) {
        //#if MC>=11500
        return mc.getWindow();
        //#else
        //$$ return mc.window;
        //#endif
    }
    //#else
    //$$ public static ScaledResolution newScaledResolution(Minecraft mc) {
        //#if MC>=10809
        //$$ return new ScaledResolution(mc);
        //#else
        //$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
        //#endif
    //$$ }
    //#endif

    public static void addDetail(CrashReportSection category, String name, Callable<String> callable) {
        //#if MC>=10904
        //#if MC>=11200
        category.add(name, callable::call);
        //#else
        //$$ category.setDetail(name, callable::call);
        //#endif
        //#else
        //$$ category.addCrashSectionCallable(name, callable);
        //#endif
    }

    public static void drawRect(int right, int bottom, int left, int top) {
        //#if MC>=10800
        Tessellator tessellator = Tessellator.getInstance();
        //#else
        //$$ Tessellator tessellator = Tessellator.instance;
        //#endif
        //#if MC>=10904
        BufferBuilder vertexBuffer = tessellator.getBuffer();
        //#else
        //#if MC>=10800
        //$$ WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
        //#else
        //$$ Tessellator vertexBuffer = tessellator;
        //#endif
        //#endif
        //#if MC>=10809
        //#if MC>=11700
        //$$ vertexBuffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION);
        //#else
        vertexBuffer.begin(GL11.GL_QUADS, VertexFormats.POSITION);
        //#endif
        vertexBuffer.vertex(right, top, 0).next();
        vertexBuffer.vertex(left, top, 0).next();
        vertexBuffer.vertex(left, bottom, 0).next();
        vertexBuffer.vertex(right, bottom, 0).next();
        //#else
        //$$ vertexBuffer.startDrawingQuads();
        //$$ vertexBuffer.addVertex(right, top, 0);
        //$$ vertexBuffer.addVertex(left, top, 0);
        //$$ vertexBuffer.addVertex(left, bottom, 0);
        //$$ vertexBuffer.addVertex(right, bottom, 0);
        //#endif
        tessellator.draw();
    }

    public static void drawRect(int x, int y, int width, int height, ReadableColor tl, ReadableColor tr, ReadableColor bl, ReadableColor br) {
        //#if MC>=10800
        Tessellator tessellator = Tessellator.getInstance();
        BufferBuilder vertexBuffer = tessellator.getBuffer();
        //#else
        //$$ Tessellator tessellator = Tessellator.instance;
        //$$ Tessellator vertexBuffer = tessellator;
        //#endif
        //#if MC>=10809
        //#if MC>=11700
        //$$ vertexBuffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        //#else
        vertexBuffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
        //#endif
        vertexBuffer.vertex(x, y + height, 0).color(bl.getRed(), bl.getGreen(), bl.getBlue(), bl.getAlpha()).next();
        vertexBuffer.vertex(x + width, y + height, 0).color(br.getRed(), br.getGreen(), br.getBlue(), br.getAlpha()).next();
        vertexBuffer.vertex(x + width, y, 0).color(tr.getRed(), tr.getGreen(), tr.getBlue(), tr.getAlpha()).next();
        vertexBuffer.vertex(x, y, 0).color(tl.getRed(), tl.getGreen(), tl.getBlue(), tl.getAlpha()).next();
        //#else
        //$$ vertexBuffer.startDrawingQuads();
        //$$ vertexBuffer.setColorRGBA(bl.getRed(), bl.getGreen(), bl.getBlue(), bl.getAlpha());
        //$$ vertexBuffer.addVertex(x, y + height, 0);
        //$$ vertexBuffer.setColorRGBA(br.getRed(), br.getGreen(), br.getBlue(), br.getAlpha());
        //$$ vertexBuffer.addVertex(x + width, y + height, 0);
        //$$ vertexBuffer.setColorRGBA(tr.getRed(), tr.getGreen(), tr.getBlue(), tr.getAlpha());
        //$$ vertexBuffer.addVertex(x + width, y, 0);
        //$$ vertexBuffer.setColorRGBA(tl.getRed(), tl.getGreen(), tl.getBlue(), tl.getAlpha());
        //$$ vertexBuffer.addVertex(x, y, 0);
        //#endif
        tessellator.draw();
    }

    public static TextRenderer getFontRenderer() {
        return getMinecraft().textRenderer;
    }

    //#if FABRIC<=0
    //$$ public static RenderGameOverlayEvent.ElementType getType(RenderGameOverlayEvent event) {
        //#if MC>=10904
        //$$ return event.getType();
        //#else
        //$$ return event.type;
        //#endif
    //$$ }
    //$$
    //$$ public static int getMouseX(GuiScreenEvent.DrawScreenEvent.Post event) {
        //#if MC>=10904
        //$$ return event.getMouseX();
        //#else
        //$$ return event.mouseX;
        //#endif
    //$$ }
    //$$
    //$$ public static int getMouseY(GuiScreenEvent.DrawScreenEvent.Post event) {
        //#if MC>=10904
        //$$ return event.getMouseY();
        //#else
        //$$ return event.mouseY;
        //#endif
    //$$ }
    //#endif

    public static void setClipboardString(String text) {
        //#if MC>=11400
        getMinecraft().keyboard.setClipboard(text);
        //#else
        //$$ GuiScreen.setClipboardString(text);
        //#endif
    }

    public static String getClipboardString() {
        //#if MC>=11400
        return getMinecraft().keyboard.getClipboard();
        //#else
        //$$ return GuiScreen.getClipboardString();
        //#endif
    }

    //#if MC<=10710
    //$$ public static void color(float r, float g, float b) { GL11.glColor3f(r, g, b); }
    //$$ public static void color(float r, float g, float b, float a) { GL11.glColor4f(r, g, b, a); }
    //$$ public static void enableBlend() { GL11.glEnable(GL_BLEND); }
    //$$ public static void enableTexture2D() { GL11.glEnable(GL_TEXTURE_2D); }
    //$$ public static void enableAlpha() { GL11.glEnable(GL_ALPHA_TEST); }
    //$$ public static void disableTexture2D() { GL11.glDisable(GL_TEXTURE_2D); }
    //$$ public static void disableAlpha() { GL11.glDisable(GL_ALPHA_TEST); }
    //$$ public static void blendFunc(int s, int d) { GL11.glBlendFunc(s, d); }
    //$$ public static void tryBlendFuncSeparate(int l, int r, int vl, int vr) { OpenGlHelper.glBlendFunc(l, r, vl, vr); }
    //$$ public static void shadeModel(int mode) { GL11.glShadeModel(mode); }
    //$$ public static void enableColorLogic() { GL11.glEnable(GL_COLOR_LOGIC_OP); }
    //$$ public static void disableColorLogic() { GL11.glDisable(GL_COLOR_LOGIC_OP); }
    //$$ public static void colorLogicOp(int op) { GL11.glLogicOp(op); }
    //#endif

    //#if MC>=11400
    public static abstract class Keyboard {
        public static final int KEY_ESCAPE = GLFW.GLFW_KEY_ESCAPE;
        public static final int KEY_HOME = GLFW.GLFW_KEY_HOME;
        public static final int KEY_END = GLFW.GLFW_KEY_END;
        public static final int KEY_UP = GLFW.GLFW_KEY_UP;
        public static final int KEY_DOWN = GLFW.GLFW_KEY_DOWN;
        public static final int KEY_LEFT = GLFW.GLFW_KEY_LEFT;
        public static final int KEY_RIGHT = GLFW.GLFW_KEY_RIGHT;
        public static final int KEY_BACK = GLFW.GLFW_KEY_BACKSPACE;
        public static final int KEY_DELETE = GLFW.GLFW_KEY_DELETE;
        public static final int KEY_RETURN = GLFW.GLFW_KEY_ENTER;
        public static final int KEY_TAB = GLFW.GLFW_KEY_TAB;
        public static final int KEY_A = GLFW.GLFW_KEY_A;
        public static final int KEY_C = GLFW.GLFW_KEY_C;
        public static final int KEY_V = GLFW.GLFW_KEY_V;
        public static final int KEY_X = GLFW.GLFW_KEY_X;

        public static void enableRepeatEvents(boolean enabled) {
            getMinecraft().keyboard.setRepeatEvents(enabled);
        }
    }
    //#endif
}
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.element.AbstractGuiElement;
import de.johni0702.minecraft.gui.function.Clickable;
import de.johni0702.minecraft.gui.function.Focusable;
import de.johni0702.minecraft.gui.function.Tickable;
import de.johni0702.minecraft.gui.function.Typeable;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.Point;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
import de.johni0702.minecraft.gui.versions.MCVer;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.SharedConstants;
import java.util.Arrays;
import static de.johni0702.minecraft.gui.utils.Utils.clamp;
import static de.johni0702.minecraft.gui.versions.MCVer.*;
/*
 * This file is part of jGui API, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2016 johni0702 <https://github.com/johni0702>
 * Copyright (c) contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package de.johni0702.minecraft.gui.element.advanced;


//#if MC>=11400
//#else
//$$ import net.minecraft.util.ChatAllowedCharacters;
//$$ import org.lwjgl.input.Keyboard;
//#endif



public abstract class AbstractGuiTextArea<T extends AbstractGuiTextArea<T>>
        extends AbstractGuiElement<T> implements Clickable, Typeable, Tickable, IGuiTextArea<T> {
    private static final ReadableColor BACKGROUND_COLOR = new Color(160, 160, 160);
    private static final ReadableColor CURSOR_COLOR = new Color(240, 240, 240);
    private static final int BORDER = 4;
    private static final int LINE_SPACING = 2;

    private boolean focused;
    private Focusable next, previous;

    private Consumer<Boolean> focusChanged;