package gminers.glasspane.component.progress; import gminers.glasspane.component.ColorablePaneComponent; import gminers.kitchensink.ReadableNumbers; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.FieldDefaults; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; /** * Base class for all progress indicators. * * @author Aesen Vismea * @see PaneProgressBar * @see PaneProgressRing */ @FieldDefaults(level = AccessLevel.PROTECTED) @ToString @Getter @Setter public abstract class PaneProgressIndicator extends ColorablePaneComponent { /** * The font renderer for this progress indicator. */ FontRenderer renderer = Minecraft.getMinecraft().fontRendererObj; /** * Whether or not the progress text for this indicator is shown. */ boolean progressTextShown = false; /** * The custom progress text for this indicator. Set to null to use the default, as specified by the progressTextStyle. */ String progressText = null; /** * The current progress of this indicator. */ @Setter(AccessLevel.NONE) long progress = 0; /** * The maximum progress of this indicator. */ @Setter(AccessLevel.NONE) long maximumProgress = 100; /** * Whether or not this indicator should render in Indeterminate mode - in this mode, an indicator shows a repeating animation and * disregards progress and maximumProgress. */ boolean indeterminate = false; /** * Whether or not the progress text should be rendered at half size. */ boolean smallProgressText = false; /** * The color of the filled portion of this indicator. */ int filledColor = 0x55FF55; /** * The color of the filled portion of the indicator, when in Indeterminate mode. */ int indeterminateColor = 0x5555FF; /** * The style to use for the default progress text. Only regarded when progressText is null. */ ProgressTextStyle progressTextStyle = ProgressTextStyle.PERCENTAGE; @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private long lastAmount = 0; @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private String timeEstimate = "about 7 eternities remaining"; @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private long lastTimeEstimateUpdate = 0; /** * Whether or not to apply an OpenGL negation blend to the rendered progress text. */ boolean invertProgressText = true; /** * The color to use for the progress text. White (0xFFFFFF) is recommended when invertProgressText is enabled. */ int progressTextColor = 0xFFFFFF; /** * Whether or not to 'smooth' the changing of the time estimate. With this off, the time estimate will constantly change as it tries to * reflect how long it would take if it continued at exactly this pace until completion. With this on, changes in estimate will be * smoothed, making the changes less extreme. */ boolean smoothTimeEstimate = true; public PaneProgressIndicator() { color = 0x333333; } /** * The current progress of this indicator. */ public void setProgress(long progress) { if (progress < 0) { progress = 0; } if (progress > maximumProgress) { progress = maximumProgress; } this.progress = progress; } /** * The maximum progress of this indicator. */ public void setMaximumProgress(long maximumProgress) { if (maximumProgress < 1) { maximumProgress = 1; } if (progress > maximumProgress) { progress = maximumProgress; } this.maximumProgress = maximumProgress; } /** * The progress of this indicator, divided by it's maximum progress. Tip: multiply this by 100 for a percentage. */ public double getPercentage() { return (double) progress / (double) maximumProgress; } protected void drawProgressText(final float progress) { if (progressTextShown) { if (invertProgressText) { GL11.glPushMatrix(); GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ONE_MINUS_SRC_COLOR); GL11.glEnable(GL11.GL_BLEND); } String text; if (progressText != null) { text = progressText; } else { if (indeterminate) { text = "?"; } else { switch (progressTextStyle) { case DECIMAL: text = progress + ""; break; case DECIMAL_WITH_TIME_ESTIMATE: text = progress + ", " + getTimeEstimate(); break; case FRACTION: text = (Math.round(progress * maximumProgress)) + "/" + maximumProgress; break; case FRACTION_WITH_TIME_ESTIMATE: text = (Math.round(progress * maximumProgress)) + "/" + maximumProgress + ", " + getTimeEstimate(); break; case PERCENTAGE: text = (Math.round(progress * 100f)) + "%"; break; case BARE_PERCENTAGE: text = (Math.round(progress * 100f)) + ""; break; case PERCENTAGE_WITH_TIME_ESTIMATE: text = (Math.round(progress * 100f)) + "%, " + getTimeEstimate(); break; default: text = "???"; break; } } } GL11.glPushMatrix(); final int textX = (width / 2) - renderer.getStringWidth(text) / 2; final int textY = (height / 2) - (renderer.FONT_HEIGHT / 2); if (smallProgressText) { GL11.glScalef(0.5f, 0.5f, 0.0f); } renderer.drawString(text, textX, textY, progressTextColor); GL11.glPopMatrix(); if (invertProgressText) { GL11.glDisable(GL11.GL_BLEND); GL11.glPopMatrix(); } } } @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private long lastMillisEstimate = -1; protected long calculateMillisEstimate() { return (long) (((maximumProgress - progress) / Math.max(progress - lastAmount, 0.00000000000000001)) * 1000); } /** * Gets the current time estimate - e.g. "about 10 minutes"<br/> * If the progress is the same as the maximumProgress, "done" is returned. If the progress or maximumProgress is equal to 0, * "not yet started" is returned. */ public String getTimeEstimate() { if (progress == maximumProgress) { return "done"; } if (progress == 0 || maximumProgress == 0) { return "not yet started"; } if (System.currentTimeMillis() - lastTimeEstimateUpdate >= 1000) { long estimate = calculateMillisEstimate(); if (smoothTimeEstimate) { if (lastMillisEstimate > 0) { estimate = (lastMillisEstimate + lastMillisEstimate + lastMillisEstimate + estimate) / 4; } lastMillisEstimate = estimate; } timeEstimate = ("about " + ReadableNumbers.humanReadableMillis(estimate) + " remaining"); lastAmount = progress; lastTimeEstimateUpdate = System.currentTimeMillis(); } return timeEstimate; } /** * The style to use for progress text. Only acknowledged if progressText is null, but progressTextShown is true, causing the default * text to appear. * * @author Aesen Vismea * */ public enum ProgressTextStyle { /** * 0%, 20%, 50%, 100%, etc */ PERCENTAGE, /** * 0, 20, 50, 100, etc */ BARE_PERCENTAGE, /** * 0/20, 0/50, 1/20, 40/60, etc */ FRACTION, /** * 0.0, 0.24582, 0.4555, 0.333333333, 1.0, etc */ DECIMAL, /** * '1%, about 2 minutes remaining', '40%, about 1 hour remaining', etc.<br/> * Some more compact progress indicators may reject this value due to it's verbosity. */ PERCENTAGE_WITH_TIME_ESTIMATE, /** * '2/250, about 2 minutes remaining', '50/400, about 1 hour remaining', etc.<br/> * Some more compact progress indicators may reject this value due to it's verbosity. */ FRACTION_WITH_TIME_ESTIMATE, /** * '0.1, about 2 minutes remaining', '0.374623, about 1 hour remaining', etc.<br/> * Some more compact progress indicators may reject this value due to it's verbosity. */ DECIMAL_WITH_TIME_ESTIMATE } }