/* * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod * Copyright (C) 2021 cyoung06 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package kr.syeyoung.dungeonsguide.gui.elements; import kr.syeyoung.dungeonsguide.gui.MPanel; import kr.syeyoung.dungeonsguide.utils.RenderUtils; import kr.syeyoung.dungeonsguide.utils.cursor.EnumCursor; import lombok.Getter; import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.MathHelper; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL14; import java.awt.*; import java.awt.datatransfer.*; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Getter public class MTextField extends MPanel { private final Color foreground = Color.white; private final Color placeHolderColor = Color.lightGray; private String text = ""; @Setter private String placeHolder = ""; private int cursorBlickTicker = 0; private int selectionStart = 0; private int selectionEnd = 0; private int cursor = 0; private int xOffset = 0; public void edit(String str) { } @Override public Dimension getPreferredSize() { return new Dimension(-1,15); } public void setText(String text) { this.text = text; } private void setText0(String text) { this.text = text; edit(text); } private void setCursor0(int cursor) { if (cursor > text.length()) cursor = text.length(); if (cursor < 0) cursor = 0; this.cursor = cursor; int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(text.substring(0, cursor)); int cursorX = width + 3- xOffset; cursorX = MathHelper.clamp_int(cursorX,10, getBounds().width - 10); xOffset = width+ 3 - cursorX; xOffset = MathHelper.clamp_int(xOffset, 0,Math.max(0, Minecraft.getMinecraft().fontRendererObj.getStringWidth(text) - getBounds().width+10)); } @Override public void render(int absMousex, int absMousey, int relMousex0, int relMousey0, float partialTicks, Rectangle clip) { clip(clip.x - 1, clip.y - 1, clip.width +2, clip.height + 2); Gui.drawRect(0,0,getBounds().width, getBounds().height, isFocused ? Color.white.getRGB() : Color.gray.getRGB()); Gui.drawRect(1,1,getBounds().width - 1, getBounds().height - 1, Color.black.getRGB()); Minecraft mc = Minecraft.getMinecraft(); clip(clip.x + 1, clip.y + 1, clip.width - 2, clip.height - 2); FontRenderer fr = mc.fontRendererObj; int y = (getBounds().height - fr.FONT_HEIGHT) / 2; GlStateManager.enableBlend(); GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); fr.drawString(text, 3 - xOffset, y, foreground.getRGB()); if (text.isEmpty()) fr.drawString(placeHolder, 3, y, placeHolderColor.getRGB()); // draw selection if (isFocused) { if (selectionStart != -1) { int startX = fr.getStringWidth(text.substring(0, selectionStart)) - xOffset; int endX = fr.getStringWidth(text.substring(0, selectionEnd)) - xOffset; Gui.drawRect(3 + startX, y, 3 + endX, y + fr.FONT_HEIGHT, 0xFF00FF00); GlStateManager.enableBlend(); GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); fr.drawString(text.substring(selectionStart, selectionEnd), 3 + startX, y, foreground.getRGB()); } // draw cursor if (cursor != -1) { if (cursor > text.length()) setCursor0(text.length()); int x = fr.getStringWidth(text.substring(0, cursor)) - xOffset; cursorBlickTicker++; if (cursorBlickTicker < 10) Gui.drawRect(3 + x, y, 4 + x, y + fr.FONT_HEIGHT, 0xFFFFFFFF); if (cursorBlickTicker == 20) cursorBlickTicker = 0; } } } @Override public void mouseClicked(int absMouseX, int absMouseY, int relMouseX, int relMouseY, int mouseButton) { Rectangle actualField = new Rectangle(1, 3,getBounds().width - 2, getBounds().height - 6); if (!actualField.contains(relMouseX, relMouseY)) return; if (!lastAbsClip.contains(absMouseX, absMouseY)) return; if (getTooltipsOpen() > 0) return; int relStartT = relMouseX-3; int offseted = relStartT + xOffset; selectionStart = -1; FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; for (int i = 0; i < text.length(); i++) { int totalWidth = fr.getStringWidth(text.substring(0, i)); if (offseted < totalWidth) { setCursor0(i); return; } } setCursor0(text.length()); } @Override public void mouseClickMove(int absMouseX, int absMouseY, int relMouseX, int relMouseY, int clickedMouseButton, long timeSinceLastClick) { if (!isFocused) return; selectionStart = cursor; selectionEnd = cursor; int relStartT = relMouseX-3; int offseted = relStartT + xOffset; FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; for (int i = 0; i < text.length(); i++) { int totalWidth = fr.getStringWidth(text.substring(0, i)); if (offseted < totalWidth) { if (i < cursor) { selectionStart = i; selectionEnd = cursor; } else { selectionStart = cursor; selectionEnd = i; } return; } } selectionEnd = text.length(); if (selectionStart == selectionEnd) { selectionStart = -1; } } @Override public void mouseScrolled(int absMouseX, int absMouseY, int relMouseX0, int relMouseY0, int scrollAmount) { if (!isFocused) return; if (scrollAmount > 0) { xOffset += 5; } else if (scrollAmount < 0){ xOffset -= 5; } if (xOffset < 0) { xOffset = 0; } int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(text); int overflow = getBounds().width - 3 - width; if (overflow >= 0) { xOffset = 0; } else if (width - xOffset + 10 < getBounds().width) { xOffset = width - getBounds().width+10; } } private Map callKeyHeldIfAfter = new HashMap<>(); @Override public void keyHeld(char typedChar, int keyCode, long heldMS) { if (!isFocused) return; Long callAfter = callKeyHeldIfAfter.get(keyCode); if (callAfter == null) return; if (callAfter <= System.currentTimeMillis()) this.keyPressed(typedChar, keyCode); } @Override public void keyReleased(char typedChar, int keyCode, long heldMS) { callKeyHeldIfAfter.remove(keyCode); } @Override public void keyPressed(char typedChar, int keycode) { if (!isFocused) return; if (callKeyHeldIfAfter.containsKey(keycode)) callKeyHeldIfAfter.put(keycode, System.currentTimeMillis()+50); else callKeyHeldIfAfter.put(keycode, System.currentTimeMillis() + 750); if (selectionStart == -1) { if (keycode == 199) { // home setCursor0(0); xOffset = 0; return; } if (keycode == 207) { // end setCursor0(text.length()); int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(text); xOffset = Integer.max(0, width - getBounds().width+10); return; } if (keycode == 203) { // left setCursor0(this.cursor-1);; if (cursor < 0) setCursor0(0); return; } if (keycode == 205) { // right setCursor0(this.cursor+1); if (cursor > text.length()) setCursor0(text.length()); return; } // backspace if (keycode == 14 && cursor > 0) { setText0(this.text.substring(0, cursor-1) + this.text.substring(cursor)); setCursor0(this.cursor-1); return; } //del if (keycode == 211 && cursor < text.length()) { setText0(this.text.substring(0, cursor) + this.text.substring(cursor+1)); return; } // paste boolean shouldPaste = false; if (keycode == 47) { if (Minecraft.isRunningOnMac) { // mac if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) { shouldPaste = true; } } else { // literally everything else if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) { shouldPaste = true; } } } if (shouldPaste) { Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { Object theText = transferable.getTransferData(DataFlavor.stringFlavor); setText0( this.text.substring(0, this.cursor) + theText + this.text.substring(this.cursor)); cursor += theText.toString().length(); } catch (UnsupportedFlavorException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return; } // text if (isPrintableChar(typedChar)) { setText0( this.text.substring(0, this.cursor) + typedChar + this.text.substring(this.cursor)); this.setCursor0(this.cursor+1);; return; } } else { if (keycode == 199) { // home setCursor0(0); selectionStart = -1; xOffset =0; return; } if (keycode == 207) { // end selectionStart = -1; setCursor0(text.length()); int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(text); xOffset = Integer.max(0, width - getBounds().width+10); return; } if (keycode == 203) { // left setCursor0(selectionStart); selectionStart = -1; return; } if (keycode == 205) { // right setCursor0(selectionEnd); selectionStart = -1; return; } // backspace if (keycode == 14 && cursor > 0) { setText0(this.text.substring(0, selectionStart) + this.text.substring(selectionEnd)); setCursor0(selectionStart); selectionStart = -1; return; } //del if (keycode == 211 && cursor < text.length()) { setText0(this.text.substring(0, selectionStart) + this.text.substring(selectionEnd)); setCursor0(selectionStart); selectionStart = -1; return; } // paste boolean shouldPaste = false; if (keycode == 47) { if (Minecraft.isRunningOnMac) { // mac if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) { shouldPaste = true; } } else { // literally everything else if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) { shouldPaste = true; } } } if (shouldPaste) { Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { Object theText = transferable.getTransferData(DataFlavor.stringFlavor); setText0( this.text.substring(0, this.selectionStart) + theText + this.text.substring(this.selectionEnd)); setCursor0(this.selectionStart + theText.toString().length()); } catch (UnsupportedFlavorException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } selectionStart = -1; } return; } boolean shouldCopy = false; if (keycode == 46) { if (Minecraft.isRunningOnMac) { // mac if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) { shouldCopy = true; } } else { // literally everything else if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) { shouldCopy = true; } } } if (shouldCopy) { StringSelection selection = new StringSelection(text.substring(selectionStart, selectionEnd)); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(selection, selection); return; } // text if (isPrintableChar(typedChar)) { setText0( this.text.substring(0, this.selectionStart) + typedChar + this.text.substring(this.selectionEnd)); this.setCursor0(this.selectionStart + 1); selectionStart = -1; return; } } } public boolean isPrintableChar( char c ) { Character.UnicodeBlock block = Character.UnicodeBlock.of( c ); return (!Character.isISOControl(c)) && c != KeyEvent.CHAR_UNDEFINED && block != null && block != Character.UnicodeBlock.SPECIALS; } @Override public void mouseMoved(int absMouseX, int absMouseY, int relMouseX0, int relMouseY0) { if (lastAbsClip.contains(absMouseX, absMouseY)) setCursor(EnumCursor.BEAM_CURSOR); } }